|
14 | 14 |
|
15 | 15 | ## 准备数据
|
16 | 16 | 准备在 链表操作中需要用到的变量及类。
|
| 17 | +```java |
| 18 | +class DATA{ |
| 19 | + String key; // 结点的关键字 |
| 20 | + String name; |
| 21 | + int age; |
| 22 | +} |
17 | 23 |
|
18 |
| - class DATA{ |
19 |
| - String key; // 结点的关键字 |
20 |
| - String name; |
21 |
| - int age; |
22 |
| - } |
23 |
| - |
24 |
| - /* |
25 |
| - * 定义链表结构 |
26 |
| - * Chain-type storage structure |
27 |
| - */ |
28 |
| - class CLType{ |
29 |
| - DATA nodeData = new DATA(); |
30 |
| - CLType nextNode; |
31 |
| - } |
| 24 | +/* |
| 25 | + * 定义链表结构 |
| 26 | + * Chain-type storage structure |
| 27 | + */ |
| 28 | +class CLType{ |
| 29 | + DATA nodeData = new DATA(); |
| 30 | + CLType nextNode; |
| 31 | +} |
| 32 | +``` |
32 | 33 | 上述代码定义了链表数据元素的类DATA及链表的类CLType。结点的具体数据保存在一个类DATA中,而引用nextNode用来指向下一个结点。
|
33 | 34 |
|
34 | 35 | 其实可以认为该链表是一个班级学生的记录,和上面顺序表所完成的工作类似。
|
35 | 36 | ## 追加结点
|
36 | 37 | 追加结点即在链表末尾增加一个结点。表尾结点的地址部分原来保存的是空地址null,此时需要将其设置为新增加结点的地址(即原表尾结点指向新增结点),然后将新增结点的地址部分设置为空地址null,即新增节点成为表尾
|
37 | 38 |
|
38 | 39 | 由于一般情况下,链表只有一个头引用head,要在末尾添加结点就需要从头引用head开始逐个检查,直到找到最后一个结点(即表尾)。
|
39 |
| - |
40 |
| - CLType CLAddEnd(CLType head, DATA nodeData){ |
41 |
| - CLType node,htemp; |
42 |
| - if((node=new CLType())==null){ |
43 |
| - System.out.println("申请内存失败!\n"); |
44 |
| - return null; |
45 |
| - }else{ |
46 |
| - node.nodeData = nodeData; // 在新申请的结点中保存数据 |
47 |
| - node.nextNode = null; // 设置结点引用为空,即为表尾 |
48 |
| - if(head == null){ // 当head为空时,加入的新结点即为头引用 |
49 |
| - head = node; |
50 |
| - return head; |
51 |
| - } |
52 |
| - htemp = head; |
53 |
| - while(htemp.nextNode != null){ // 查找链表的末尾 |
54 |
| - htemp = htemp.nextNode; |
55 |
| - } |
56 |
| - htemp.nextNode = node; // 在链表的末尾插入该结点 |
57 |
| - return head; // 返回指代整个链表的头引用 |
| 40 | +```java |
| 41 | +CLType CLAddEnd(CLType head, DATA nodeData){ |
| 42 | + CLType node,htemp; |
| 43 | + if((node=new CLType())==null){ |
| 44 | + System.out.println("申请内存失败!\n"); |
| 45 | + return null; |
| 46 | + }else{ |
| 47 | + node.nodeData = nodeData; // 在新申请的结点中保存数据 |
| 48 | + node.nextNode = null; // 设置结点引用为空,即为表尾 |
| 49 | + if(head == null){ // 当head为空时,加入的新结点即为头引用 |
| 50 | + head = node; |
| 51 | + return head; |
58 | 52 | }
|
| 53 | + htemp = head; |
| 54 | + while(htemp.nextNode != null){ // 查找链表的末尾 |
| 55 | + htemp = htemp.nextNode; |
| 56 | + } |
| 57 | + htemp.nextNode = node; // 在链表的末尾插入该结点 |
| 58 | + return head; // 返回指代整个链表的头引用 |
59 | 59 | }
|
| 60 | +} |
| 61 | +``` |
60 | 62 | 在上述代码中,输入参数head为链表头引用,输入参数nodeData为结点保存的数据。程序中,使用new关键字申请保存结点数据的内存空间,如果分配内存成功,node中将保存指向该内存区域的引用。然后,将传入的nodeData保存到申请的内存区域,并设置该结点指向下一结点的引用值为null,最后将该结点链接到链表的末尾。
|
61 | 63 | ## 插入头结点
|
62 | 64 | 插入头结点即在链表的首部添加结点的过程。
|
63 | 65 | 步骤如下:
|
64 | 66 | 1. 分配内存空间,保存新增的结点。
|
65 | 67 | - 使新增结点指向头引用head所指向的结点。
|
66 | 68 | - 使头引用head指向新增结点。
|
67 |
| - |
68 |
| - CLType CLAddFirst(CLType head, DATA nodeData){ |
69 |
| - CLType node; |
70 |
| - if((node = new CLType())==null){ |
71 |
| - System.out.println("申请内存失败!\n"); |
72 |
| - return null; |
73 |
| - }else{ |
74 |
| - node.nodeData = nodeData; // 保存数据 |
75 |
| - node.nextNode = head; // 新加入的头结点的nextNode指向头引用所指的结点 |
76 |
| - head = node; // 头引用指向新节点 |
77 |
| - return head; |
78 |
| - } |
| 69 | +```java |
| 70 | +CLType CLAddFirst(CLType head, DATA nodeData){ |
| 71 | + CLType node; |
| 72 | + if((node = new CLType())==null){ |
| 73 | + System.out.println("申请内存失败!\n"); |
| 74 | + return null; |
| 75 | + }else{ |
| 76 | + node.nodeData = nodeData; // 保存数据 |
| 77 | + node.nextNode = head; // 新加入的头结点的nextNode指向头引用所指的结点 |
| 78 | + head = node; // 头引用指向新节点 |
| 79 | + return head; |
79 | 80 | }
|
80 |
| - |
| 81 | +} |
| 82 | +``` |
81 | 83 | ## 查找结点
|
82 | 84 | 通过关键字进行查询。
|
83 |
| - |
84 |
| - CLType CLFindNode(CLType head, String key){ |
85 |
| - CLType htemp; |
86 |
| - htemp = head; |
87 |
| - while(htemp!=null){ // 循环遍历,寻找关键字匹配的结点 |
88 |
| - if(htemp.nodeData.key.compareTo(key)==0){ |
89 |
| - return htemp; |
90 |
| - } |
91 |
| - htemp = htemp.nextNode; |
| 85 | +```java |
| 86 | +CLType CLFindNode(CLType head, String key){ |
| 87 | + CLType htemp; |
| 88 | + htemp = head; |
| 89 | + while(htemp!=null){ // 循环遍历,寻找关键字匹配的结点 |
| 90 | + if(htemp.nodeData.key.compareTo(key)==0){ |
| 91 | + return htemp; |
92 | 92 | }
|
93 |
| - return null; |
| 93 | + htemp = htemp.nextNode; |
94 | 94 | }
|
| 95 | + return null; |
| 96 | +} |
| 97 | +``` |
95 | 98 | 在上述代码中,输入参数head为链表的头引用,输入参数key是用来在链表中进行查找结点的关键字。程序中,首先从链表头引用开始,对结点进行逐个比较,直到查找到。找到关键字相同的结点后,返回该结点的引用,方便调用程序处理。
|
96 | 99 | ## 插入结点
|
97 | 100 | 插入结点就是在链表中间部分的指定位置增加一个结点。插入结点的操作步骤如下:
|
98 | 101 | 1. 分配内存空间,保存新增的结点。
|
99 | 102 | - 找到要插入的逻辑位置,也就是那两个结点之间。
|
100 | 103 | - 使新增节点指向原插入位置所指向的结点,并修改插入位置结点的引用,使其指向新增结点。
|
101 |
| - |
102 |
| - CLType CLInsertNode(CLType head, String findKey, DATA nodeData){ // 插入关键字结点 |
103 |
| - CLType node, nodetemp; |
104 |
| - if((node = new CLType())==null){ |
105 |
| - System.out.println("申请内存失败!\n"); |
106 |
| - return null; |
107 |
| - } |
108 |
| - node.nodeData = nodeData; |
109 |
| - nodetemp = CLFindNode(head, findKey); // 获取插入位置关键字所指代的结点 |
110 |
| - if(nodetemp == null){ |
111 |
| - System.out.println("未找到正确的插入位置!\n"); |
112 |
| - }else{ |
113 |
| - node.nextNode = nodetemp.nextNode; // 新插入的节点的nextNode指向关键字结点的下一结点 |
114 |
| - nodetemp.nextNode = node; // 设置关键结点指向新插入结点 |
115 |
| - } |
116 |
| - return head; |
| 104 | +```java |
| 105 | +CLType CLInsertNode(CLType head, String findKey, DATA nodeData){ // 插入关键字结点 |
| 106 | + CLType node, nodetemp; |
| 107 | + if((node = new CLType())==null){ |
| 108 | + System.out.println("申请内存失败!\n"); |
| 109 | + return null; |
117 | 110 | }
|
| 111 | + node.nodeData = nodeData; |
| 112 | + nodetemp = CLFindNode(head, findKey); // 获取插入位置关键字所指代的结点 |
| 113 | + if(nodetemp == null){ |
| 114 | + System.out.println("未找到正确的插入位置!\n"); |
| 115 | + }else{ |
| 116 | + node.nextNode = nodetemp.nextNode; // 新插入的节点的nextNode指向关键字结点的下一结点 |
| 117 | + nodetemp.nextNode = node; // 设置关键结点指向新插入结点 |
| 118 | + } |
| 119 | + return head; |
| 120 | +} |
| 121 | +``` |
118 | 122 | ## 删除结点
|
119 | 123 | 删除结点就是将链表中的某个结点数据删除。删除结点的操作步骤如下:
|
120 | 124 | 1. 查找需要删除的结点。
|
121 | 125 | - 使前一结点指向当前结点的下一结点。
|
122 | 126 | - 删除结点。
|
123 |
| - |
124 |
| - int CLDeleteNode(CLType head, String key){ // 删除关键字结点 |
125 |
| - CLType htemp,tempnode; |
126 |
| - if(head == null){ |
127 |
| - return 0; |
128 |
| - } |
129 |
| - htemp = head; |
130 |
| - tempnode = head; |
131 |
| - while(htemp != null){ |
132 |
| - if(htemp.nodeData.key.compareTo(key)==0){ // 遍历查找 |
133 |
| - tempnode.nextNode = htemp.nextNode; // 当前结点的引用指向下一个结点的引用,以此删除htemp结点 |
134 |
| - return 1; |
135 |
| - } |
136 |
| - tempnode = htemp; // 保存当前结点 |
137 |
| - htemp = htemp.nextNode; // 指向下一个结点 |
138 |
| - } |
| 127 | +```java |
| 128 | +int CLDeleteNode(CLType head, String key){ // 删除关键字结点 |
| 129 | + CLType htemp,tempnode; |
| 130 | + if(head == null){ |
139 | 131 | return 0;
|
140 | 132 | }
|
| 133 | + htemp = head; |
| 134 | + tempnode = head; |
| 135 | + while(htemp != null){ |
| 136 | + if(htemp.nodeData.key.compareTo(key)==0){ // 遍历查找 |
| 137 | + tempnode.nextNode = htemp.nextNode; // 当前结点的引用指向下一个结点的引用,以此删除htemp结点 |
| 138 | + return 1; |
| 139 | + } |
| 140 | + tempnode = htemp; // 保存当前结点 |
| 141 | + htemp = htemp.nextNode; // 指向下一个结点 |
| 142 | + } |
| 143 | + return 0; |
| 144 | +} |
| 145 | +``` |
141 | 146 | 注意,此时被删除结点仍然保存在内存中,接着执行赋值null操作,用来释放被删除节点所占用的内存空间。
|
142 | 147 | ## 计算链表长度
|
143 | 148 | 统计链表结构中结点的数量,由于链表结构在物理上不是连续存储的,因此,需要遍历整个链表来对结点数量进行累加。
|
144 |
| - |
145 |
| - int CLLength(CLType head){ //返回结点总数 |
146 |
| - CLType htemp; |
147 |
| - int len = 0; |
148 |
| - htemp = head; |
149 |
| - while(htemp!=null){ // 循环遍历累计总结点数 |
150 |
| - len++; |
151 |
| - htemp=htemp.nextNode; |
152 |
| - } |
153 |
| - return len; |
| 149 | +```java |
| 150 | +int CLLength(CLType head){ //返回结点总数 |
| 151 | + CLType htemp; |
| 152 | + int len = 0; |
| 153 | + htemp = head; |
| 154 | + while(htemp!=null){ // 循环遍历累计总结点数 |
| 155 | + len++; |
| 156 | + htemp=htemp.nextNode; |
154 | 157 | }
|
| 158 | + return len; |
| 159 | +} |
| 160 | +``` |
155 | 161 | 程序中通过while循环来遍历整个链表,从而累加数量并返回。
|
156 | 162 |
|
157 | 163 | ## 显示所有结点
|
158 |
| - |
159 |
| - void CLAllNode(CLType head){ // 遍历链表输出所有数据 |
160 |
| - CLType htemp; |
161 |
| - htemp = head; |
162 |
| - System.out.printf("\n当前链表共有%d个结点。链表所有数据如下:\n",CLLength(head)); |
163 |
| - while(htemp != null){ |
164 |
| - System.out.printf("结点(%s,%s,%d)\n",htemp.nodeData.key, htemp.nodeData.name, htemp.nodeData.age); |
165 |
| - htemp = htemp.nextNode; |
166 |
| - } |
| 164 | +```java |
| 165 | +void CLAllNode(CLType head){ // 遍历链表输出所有数据 |
| 166 | + CLType htemp; |
| 167 | + htemp = head; |
| 168 | + System.out.printf("\n当前链表共有%d个结点。链表所有数据如下:\n",CLLength(head)); |
| 169 | + while(htemp != null){ |
| 170 | + System.out.printf("结点(%s,%s,%d)\n",htemp.nodeData.key, htemp.nodeData.name, htemp.nodeData.age); |
| 171 | + htemp = htemp.nextNode; |
167 | 172 | }
|
168 |
| - |
| 173 | +} |
| 174 | +``` |
169 | 175 | ## 链表操作实例
|
170 | 176 | 完整实例源码:
|
171 | 177 | [LinkedList.java](./LinkedList.java)
|
0 commit comments