0%

数据结构的单向链表的各项操作实现

着重实现《剑指Offer》上面的单向链表操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
//数据结构
struct ListNode
{
int data;
ListNode *next;
ListNode(int x) :data(x), next(NULL) {}
};
//在链表尾部添加结点
void AddToTail(ListNode **pHead, int value)
{
ListNode *pNew = new ListNode(value);
assert(pNew != NULL);

if (NULL == *pHead)//原链表为空
*pHead = pNew;
else
{
ListNode *pNode = *pHead;

while (pNode->next)
pNode = pNode->next;

pNode->next = pNew;
}

}
/*单向链表删除操作需要找到前一个结点
二级指针操作是因为会可能修改头结点指针*/
void RemoveNode(ListNode **pHead, int value)
{
if (NULL == pHead || NULL == *pHead)
return;

ListNode *pToDeleted = NULL;
if ((*pHead)->data == value)
{
pToDeleted = *pHead;
*pHead = (*pHead)->next;
}
else
{
ListNode *pNode = *pHead;

while (pNode->next && pNode->next->data != value)//找到待删除结点的前结点
pNode = pNode->next;

if (pNode->next && pNode->next->data == value)
{
pToDeleted = pNode->next;
pNode->next = pNode->next->next;
}
}

if (pToDeleted != NULL)
{
delete pToDeleted;
pToDeleted = NULL;
}

}
//反向打印:递归。链表过长会导致栈溢出
void PrintReverse(ListNode *pHead)
{
if (pHead)
{
PrintReverse(pHead->next);
cout << pHead->data << endl;
}
}

//反向打印:栈
void PrintReverse_iteratively(ListNode *pHead)
{
stack<ListNode *> nodes;

ListNode *pNode = pHead;
while (pNode)
{
nodes.push(pNode);
pNode = pNode->next;
}

while (!nodes.empty())
{
pNode = nodes.top();
cout << pNode->data << endl;
nodes.pop();
}

}
/*
给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点
前提是这个结点是存在于这个链表中,这个由调用者保证

其实现思想:把下一个结点的内容复制到待删除结点上覆盖原有的内容,然后删除下一个结点。
就是操作结点的数据而不是链表的链接关系。平衡二叉树的平衡旋转也是采用这个思想
*/
void DeleteNode(ListNode **pHead, ListNode *pToBeDeleted)
{
if (NULL == pHead || NULL == *pHead)
return;

//要删除的结点不是尾节点
if (pToBeDeleted->next)
{
ListNode *pNext = pToBeDeleted->next;
pToBeDeleted->data = pNext->data;//操作数据而非链接关系
pToBeDeleted->next = pNext->next;

delete pNext;
pNext = NULL;
}
//链表只有一个结点
else if (*pHead == pToBeDeleted)
{
delete pToBeDeleted;
pToBeDeleted = NULL;
*pHead = NULL;
}
//链表有多个结点,且待删除结点是尾节点
else//这部分时间复杂度为O(n),但总的平均时间复杂度为O(1)
{
ListNode *pNode = *pHead;
while (pNode->next != pToBeDeleted)
pNode = pNode->next;

pNode->next = NULL;
delete pToBeDeleted;
pToBeDeleted = NULL;
}

}
/*输入一个链表,输出该链表中倒数第 k 个结点
思路一:栈*/
ListNode* PrintKNode(ListNode *pHead, int k)
{
if (NULL == pHead || k < 1)
return NULL;

stack<ListNode *> nodes;
ListNode *pNode = pHead;
while (pNode)
{
nodes.push(pNode);
pNode = pNode->next;
}

while (!nodes.empty())
{
--k;
if (0 == k)
{
pNode = nodes.top();
return pNode;
}
nodes.pop();
}
return NULL;

}
/*思路二:快慢指针。快慢指针之间的距离为k-1,当快指针到达尾结点时,慢指针恰好是倒数第k个结点*/
ListNode* FindKthToTail(ListNode *pHead, int k)
{
if (NULL == pHead || k < 1)
return NULL;

ListNode *pAhead = pHead;
ListNode *pBehind = NULL;

for (int i = 0; i < k - 1; ++i)
{
if (pAhead->next)
pAhead = pAhead->next;
else
return NULL;
}

pBehind = pHead;
while (pAhead->next)
{
pAhead = pAhead->next;
pBehind = pBehind->next;
}

return pBehind;

}
/*反转链表:输入一个链表的头结点,反转该链表并输出反转后链表的头结点
充分考虑异常情况,尤其是清楚最后反转后的头结点*/
ListNode* ReverseList(ListNode *pHead)
{
if (NULL == pHead || pHead->next == NULL)
return pHead;

ListNode *pPrev = NULL;
ListNode *pNode = pHead;
while (pNode)
{
ListNode *pNext = pNode->next;

pNode->next = pPrev;
pPrev = pNode;//这就是最后反转后的头结点
pNode = pNext;
}
return pPrev;

}
/*合并两个排序的链表,合并后的链表仍是排序好的(递增)*/
/*未排序区的两个链表值小的那个头结点,将是未排序区合并后的头结点,同时也将作为排序区的尾结点。
以此递归,每次只需比较剩下未排序的两个链表的头结点*/
ListNode* MergeList(ListNode *pHead1, ListNode *pHead2)
{
if (NULL == pHead1)
return pHead2;
else if (NULL == pHead2)
return pHead1;

ListNode *pMergedHead = NULL;

if (pHead1->data < pHead2->data)
{
pMergedHead = pHead1;
pMergedHead->next = MergeList(pHead1->next, pHead2);
}
else
{
pMergedHead = pHead2;
pMergedHead->next = MergeList(pHead1, pHead2->next);
}
return pMergedHead;

}
/*输入两个链表,找出它们的第一个公共结点
下面链表L1有6个结点,L2有4个结点,其第一个公共结点为mn1*/
/*
L1: n1 -> n2 -> n3 -> n4
\
——> mn1 -> mn2
/
L2: m1 -> m2
*/
/*
实现思想:长的链表先前进若干结点到与短链表等长位置,然后同步前进
充分考虑异常情况
*/
int GetLength(ListNode *pHead)
{
int leng = 0;
while (pHead)
{
leng++;
pHead = pHead->next;
}
return leng;
}

ListNode* FindFirstCommonNode(ListNode *L1, ListNode *L2)
{
if (NULL == L1 || NULL == L2)
return NULL;
int leng1 = GetLength(L1);
int leng2 = GetLength(L2);

int distance = (leng1 > leng2) ? leng1 - leng2 : leng2 - leng1;
ListNode *pFirst = NULL, *pSecond = NULL;
if (leng1 > leng2)
{
pFirst = L1;
pSecond = L2;
}
else
{
pFirst = L2;
pSecond = L1;
}

while (distance--)//考虑非公共部分长度一样情况
{
pFirst = pFirst->next;
}

while (pFirst && pSecond)
{
if (pFirst == pSecond)//寻找第一个公共结点
return pFirst;
pFirst = pFirst->next;
pSecond = pSecond->next;
}
return NULL;//不存在公共结点情况

}

/*链表中环的入口结点
最常规解法:快慢指针,关键在于定位环的入口结点,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
ListNode* EntryNodeOfLoop(ListNode *pHead)
{
if (NULL == pHead)
return NULL;

ListNode *pFast = pHead;
ListNode *pSlow = pHead;

//判断是否有环
while (pFast->next)//两种情况跳出循环
{
pFast = pFast->next->next;
pSlow = pSlow->next;

if (NULL == pFast)
return NULL;
if (pFast == pSlow)//有环
break;
}

if (NULL == pFast->next)//没环
return NULL;

//寻找环的入口结点
/*快指针移动至链表头结点,同时两结点以相同速度再次相遇就是环的开始节点
http://blog.csdn.net/wenqian1991/article/details/17452715 */
pFast = pHead;
while (pFast != pSlow)
{
pFast = pFast->next;
pSlow = pSlow->next;
}

return pFast;

}