剑指offer练习-链表

由于最近太忙,几个链表的题目都写在了一个解决方案里了,代码可能有点乱,以下先提交代码,思路都在注释里,后期有时间了再整理笔记

 

  1 // printListFromTailToHead.cpp: 定义控制台应用程序的入口点。
  2 //
  3 
  4 #include "stdafx.h"
  5 #include<iostream>
  6 #include<vector>
  7 #include<queue>
  8 #include<set>
  9 #include<stack>
 10 #include<malloc.h>
 11 #include<map>
 12 #include<debugapi.h>
 13 using namespace std;
 14 
 15 #define N 40
 16 struct ListNode {
 17 	int val;
 18 	struct ListNode *next;
 19 
 20 };
 21 struct RandomListNode {//复杂链表
 22 	int label;
 23 	struct RandomListNode *next, *random;
 24 	RandomListNode(int x) :
 25 		label(x), next(NULL), random(NULL) {
 26 	}
 27 };
 28 class Solution {
 29 public:
 30 	vector<int> printListFromTailToHead(ListNode *head) {//将链表逆序打印
 31 		/*思路
 32 		通常,这种情况下,我们不希望修改原链表的结构。返回一个反序的链表,这就是经典的“后进先出”,我们可以使用栈实现
 33 		这种顺序。每经过一个结点的时候,把该结点放到一个栈中。
 34 		当遍历完整个链表后,再从栈顶开始逐个输出结点的值,给一个新的链表结构,这样链表就实现了反转。*/
 35 		stack<int> nodes;
 36 		ListNode *node = head;
 37 		vector<int> result;
 38 		while (node != NULL) {
 39 			nodes.push(node->val);
 40 			node = node->next;
 41 		}
 42 		while (!nodes.empty()) {
 43 			result.push_back(nodes.top());
 44 			nodes.pop();
 45 		}
 46 		return result;
 47 	}
 48 	ListNode* CreateListNode(int num, int nodes[]) {//创建链表
 49 		ListNode *p, *q;
 50 		ListNode *listnode;
 51 		listnode = (ListNode *)malloc(sizeof(ListNode));//分配空间,也可以用c++的new
 52 		listnode->next = NULL;
 53 		p = listnode;
 54 		for (int i = 0; i<num; i++) {
 55 			q = (ListNode *)malloc(sizeof(ListNode));
 56 			q->val = nodes[i];
 57 			p->next = q;
 58 			p = q;
 59 		}
 60 		p->next = NULL;
 61 		return listnode;
 62 	}
 63 	ListNode* CreateListNodeWithHead(int num, int nodes[]) {//创建链表
 64 		ListNode *p, *q;
 65 		ListNode *listnode;
 66 		listnode = (ListNode *)malloc(sizeof(ListNode));//分配空间,也可以用c++的new
 67 		//listnode->next = NULL;
 68 		p = listnode;
 69 		for (int i = 0; i<num; i++) {
 70 			q = (ListNode *)malloc(sizeof(ListNode));
 71 			q->val = nodes[i];
 72 			p->next = q;
 73 			p = q;
 74 		}
 75 		listnode = listnode->next;//这里将头结点指向下一个结点,是为了使头结点数值有意义,注意与上一个创建方法的代码的两处区别
 76 		p->next = NULL;
 77 		return listnode;
 78 	}
 79 	void PrintListNode(ListNode *listnode) {//遍历输出链表
 80 		ListNode *p = listnode->next;//因为创建的链表头结点数值为空,所以要从头结点下一个结点开始打印
 81 		//cout <<"test"<< listnode->val<<"test";
 82 		while (p!= NULL) {
 83 			cout << p->val;
 84 			p = p->next;
 85 		}
 86 		cout << endl;
 87 	}
 88 	void PrintReverseListNode(ListNode *listnode) {//遍历输出逆置后的链表,因为逆置之后的头结点数值非空,所以要从头结点开始打印
 89 		ListNode *p = listnode;
 90 		//cout << "test" << listnode->val << "test";
 91 		while (p->next!= NULL) {//在逆置的时候,逆置后的链表的尾结点是原链表的头结点,所以输出的时候不要把尾结点输出
 92 			cout << p->val;
 93 			p = p->next;
 94 		}
 95 		cout << endl;
 96 	}
 97 	ListNode* FindKthToTail(ListNode *plistnode,int k) {//找到链表中倒数第k个元素
 98 		/*思路
 99 		我们可以定义两个指针。第一个指针从链表的头指针开始遍历向前走k-1,第二个指针保持不动;从第k步开始,
100 		第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在k-1,
101 		当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第k个结点。*/
102 		if (plistnode == NULL || k ==0) {
103 			return NULL;
104 		}
105 		ListNode *palistnode;
106 		ListNode *pblistnode;
107 		palistnode = plistnode;
108 		pblistnode = plistnode;
109 		for (int i = 0; i < k-1; i++) {
110 			if(palistnode->next != NULL) {
111 				palistnode = palistnode->next;
112 			}
113 			else {
114 				return NULL;
115 			}
116 		}
117 		while (palistnode->next != NULL) {
118 			palistnode = palistnode->next;
119 			pblistnode = pblistnode->next;
120 		}
121 		return pblistnode;
122 	}
123 	ListNode* ReverseList(ListNode *head) {//将链表逆置
124 		/*思路
125         这个很简单,我们使用三个指针,分别指向当前遍历到的结点、它的前一个结点以及后一个结点。
126         在遍历的时候,做当前结点的尾结点和前一个结点的替换。
127 		*/
128 		ListNode *Cur, *Nex, *Pre;
129 		if (head == NULL)
130 			return NULL;
131 		if (head->next == NULL)
132 			return head;
133 		Cur = head;
134 		Nex = NULL;
135 		Pre = NULL;
136 		while (Cur != NULL) {
137 			Nex = Cur->next;
138 			Cur->next = Pre;
139 			Pre = Cur;
140 			Cur = Nex;
141 		}
142 		return Pre;
143 	}
144 	ListNode* Merge(ListNode *listnode1, ListNode *listnode2) {//合并两个有序链表,还可以使用递归算法合并两个有序链表
145 
146 		if (listnode1== NULL)
147 			return listnode2;
148 		if (listnode2 == NULL)
149 			return listnode1;
150 		ListNode *p = listnode1->next;//因为这两个链表创建的时候头结点数值为空,所以要从头结点下一个结点开始遍历
151 		ListNode *q = listnode2->next;
152 		ListNode *result;
153 		ListNode *t;
154 		result = (ListNode *)malloc(sizeof(ListNode));
155 		result->next = NULL;
156 		ListNode *temp = result;
157 		while (p != NULL&&q != NULL) {
158 			if (p->val < q->val) {
159 				t= (ListNode *)malloc(sizeof(ListNode));
160 				t->val = p->val;
161 				temp->next = t;
162 				temp = t;
163 				p = p->next;
164 			}
165 			else {
166 				t = (ListNode *)malloc(sizeof(ListNode));
167 				t->val = q->val;
168 				temp->next = t;
169 				temp = t;
170 				q = q->next;
171 			}
172 		}
173 		while (p != NULL) {
174 			t = (ListNode *)malloc(sizeof(ListNode));
175 			t->val = p->val;
176 			temp->next = t;
177 			temp = t;
178 			p = p->next;
179 		}
180 		while (q != NULL) {
181 			t = (ListNode *)malloc(sizeof(ListNode));
182 			t->val = q->val;
183 			temp->next = t;
184 			temp = t;
185 			q = q->next;
186 		}
187 		temp->next = NULL;
188 		return result;
189 	}
190 	ListNode* MergeWithRecursion(ListNode *listnode1, ListNode *listnode2) {//递归合并两个有序链表
191 		if (listnode1 == NULL)
192 			return listnode2;
193 		if (listnode2 == NULL)
194 			return listnode1;
195 		ListNode *listnode =NULL;
196 		if (listnode1->val < listnode2->val) {
197 			listnode = listnode1;
198 			listnode->next = MergeWithRecursion(listnode1->next, listnode2);
199 		}
200 		else {
201 			listnode = listnode2;
202 			listnode->next = MergeWithRecursion(listnode1, listnode2->next);
203 		}
204 		return listnode;
205 	}
206 	RandomListNode* CloneWithMap(RandomListNode *pHead) {//复制复杂的链表:节点可以有两个指向,一个指向下一节点,一个指向随机节点
207 		/*
208 		   方法一思路:首先复制原链表普通指针域,一次遍历即可,复制过程中将新旧链表中的节点一一对应,将其映射关系放入到
209 		   map<RandomListNode *, RandomListNode *> nodeMap中,然后复制随机指针域,先找到随机指针指向的随机节点,由于map中对应的有该随机节点对应的
210 		   新节点,所以也就找到了随机指针指向的结点对应的新节点,该新节点即为新链表前结点对应的随机指针指向的结点,然后新节点随机指针指向该节点,时间复杂度:O(N)  
211 		*/
212 		if (pHead = NULL) {
213 			return NULL;
214 		}
215 		map<RandomListNode *, RandomListNode *> nodeMap;
216 		RandomListNode *currNode = pHead;
217 		RandomListNode *newHead = NULL, *preNode = NULL, *newNode = NULL;
218 		//首先复制原链表的普通指针域,一次遍历即可完成
219 		while (currNode != NULL) {
220 			newNode = new RandomListNode(currNode->label);
221 			nodeMap[currNode] = newNode;
222 			if (preNode == NULL) {
223 				newHead = newNode;
224 			}
225 			else
226 			{
227 				preNode->next = newNode;
228 			}
229 			preNode = newNode;
230 		}
231 		//  接着复制随机指针域, 需要两次遍历
232 		currNode = pHead;
233 		newNode = newHead;
234 		while (currNode != NULL&&newNode != NULL) {
235 			RandomListNode *randNode = currNode->random;
236 			RandomListNode *newRandNode = nodeMap[randNode];
237 			newNode->random = newRandNode;//这里找到原链表对应的随机指针指向的结点后,再从map中找到对应的新节点,然后将新链表随机指针指向该新节点
238 			currNode = currNode->next;
239 			newNode = newNode->next;
240 		}
241 		return newHead;
242 	}
243 	RandomListNode* CloneTogether(RandomListNode *pHead) {
244 		/*
245 		  我们需要的就只是能够建立新节点与原节点之前的对应关系就可以了, 链表中的顺序非随机访问方式,能够很简单的通过接单的nex指针t域查找下一个节点
246 		  ,那么我们将新节点直接插入到原结点的后面,这样可以很方便的通过原来节点找到新节点,
247 		  1.遍历一遍原始链表,复制结点N对应的N’,将其插入到结点N的后面
248 		  2.确定每个随机指针的指向,只需遍历一遍链表即可确定每个结点的随机指针的指向
249 		  次遍历一遍,将原始链表和复制链表分开,奇数为原始链表,偶数为复制链表
250 		*/
251 		if (pHead = NULL) {
252 			return NULL;
253 		}
254 		RandomListNode *currNode = pHead;
255 		RandomListNode *newHead = NULL, *preNode = NULL, *newNode = NULL;
256 		while (currNode != NULL) {
257 			if ((newNode = new RandomListNode(currNode->label)) == NULL) {
258 				perror("new error:");
259 				exit(-1);
260 			}
261 			// 将新的节点newNode连接在currNode的后面
262 			newNode->next = currNode->next;
263 			currNode->next = newNode;
264 			//  指向指向下一个节点
265 			currNode = newNode->next;
266 		}
267 		//  接着复制随机指针域,原来节点的下一个位置就是其对应的新节点
268 		currNode = pHead;
269 		newNode = currNode->next;
270 		while (currNode != NULL) {
271 			RandomListNode *randNode = currNode->random;//  随机指针域randNode
272 			RandomListNode *newNode = currNode->next;
273 			if (randNode != NULL) {
274 				newNode->random = randNode->next;//新节点的随机指针指向的节点即为旧节点的随机指针指向的节点指向的下一个节点
275 			}
276 			else
277 			{
278 				newNode->random = NULL;
279 			}
280 			currNode = newNode->next;//  链表同步移动
281 		}
282 		// 将链接在一起的新旧两个链表拆分开,脱链,更新各链表的next指针
283 		currNode = pHead;
284 		newNode = newHead = pHead->next;
285 		while (currNode != NULL) {
286 			currNode->next = newNode->next;
287 			if (newNode->next != NULL) {
288 				newNode->next = newNode->next->next;
289 			}
290 			else {
291 				newNode->next = NULL;
292 			}
293 			currNode = currNode->next;
294 			newNode = newNode->next;
295 		}
296 		return newHead;
297 	}
298 	void printReverse() {//用于用户交互来转置链表
299 		int num;
300 		//ListNode *listnode;//要创建的链表
301 		int listnodes[N];
302 		cout << "请输入链表大小:" << endl;
303 		cin >> num;
304 		cout << "请依次输入链表每个节点数值:" << endl;
305 		for (int i = 0; i<num; i++) {
306 			cin >> listnodes[i];
307 		}
308 		Solution solution;
309 		ListNode *listnode = solution.CreateListNode(num, listnodes);
310 		solution.PrintListNode(listnode);
311 		ListNode *result = solution.ReverseList(listnode);
312 		solution.PrintReverseListNode(result);
313 	}
314 	void printMerge() {//用于用户交互有序链表合并过程
315 		Solution solution;
316 		int num1, num2;
317 		ListNode *listnode1, *listnode2;
318 		int listnodes1[N], listnodes2[N];
319 		ListNode *result2;
320 		cout << "请输入第一个链表大小:" << endl;
321 		cin >> num1;
322 		cout << "请按照数值递增顺序依次输入第一个链表每个节点数值:" << endl;
323 		for (int i = 0; i<num1; i++) {
324 			cin >> listnodes1[i];
325 		}
326 		//Solution solution;
327 		listnode1 = solution.CreateListNode(num1, listnodes1);
328 		solution.PrintListNode(listnode1);
329 		cout << "请输入第二个链表大小:" << endl;
330 		cin >> num2;
331 		cout << "请按照数值递增顺序依次输入第二个链表每个节点数值:" << endl;
332 		for (int i = 0; i<num2; i++) {
333 			cin >> listnodes2[i];
334 		}
335 		listnode2 = solution.CreateListNode(num2, listnodes2);
336 		solution.PrintListNode(listnode2);
337 		cout << "合并两个有序链表" << endl;
338 		//如果使用递归调用的话要把链表移到头结点的额下一个结点,不然会把头结点的空值也合并进去了
339 		listnode1 = listnode1->next;
340 		listnode2 = listnode2->next;
341 		result2 = solution.MergeWithRecursion(listnode1, listnode2);
342 		solution.PrintReverseListNode(result2);
343 	}
344 	void printprintListFromTailToHead() {//用户交互从尾到头打印链表过程
345 		int num;
346 		//ListNode *listnode;//要创建的链表
347 		int listnodes[N];
348 		cout << "请输入链表大小:" << endl;
349 		cin >> num;
350 		cout << "请依次输入链表每个节点数值:" << endl;
351 		for (int i = 0; i<num; i++) {
352 			cin >> listnodes[i];
353 		}
354 		Solution solution;
355 		ListNode *listnode = solution.CreateListNode(num, listnodes);
356 		solution.PrintListNode(listnode);
357 		vector<int> result;
358 		result = solution.printListFromTailToHead(listnode);
359 		cout << endl << "逆序输出该链表" << endl;
360 		for (int i = 0; i < num; i++) {
361 			cout << result[i];
362 		}
363 		cout << endl;
364 	}
365 	void printFindKthToTail() {//用户交互打印倒数第K个值
366 		int num;
367 		//ListNode *listnode;//要创建的链表
368 		int listnodes[N];
369 		cout << "请输入链表大小:" << endl;
370 		cin >> num;
371 		cout << "请依次输入链表每个节点数值:" << endl;
372 		for (int i = 0; i<num; i++) {
373 			cin >> listnodes[i];
374 		}
375 		Solution solution;
376 		ListNode *listnode = solution.CreateListNode(num, listnodes);
377 		solution.PrintListNode(listnode); 
378 		int k;
379 		cout << "请输入K值" << endl;
380 		cin >> k;
381 		ListNode *result = solution.FindKthToTail(listnode, k);
382 		cout << result->val;
383 	}
384 };
385 int main() {
386 	/*int num;
387 	//ListNode *listnode;//要创建的链表
388 	int listnodes[N];	
389 	cout << "请输入链表大小:" << endl;
390 	cin >> num;
391 	cout << "请依次输入链表每个节点数值:" << endl;
392 	for (int i = 0; i<num; i++) {
393 		cin >> listnodes[i];
394 	}
395 	Solution solution;
396 	ListNode *listnode=solution.CreateListNode(num, listnodes);
397 	solution.PrintListNode(listnode);*/
398 	Solution solution;
399 	solution.printReverse();
400 }

猜你喜欢

转载自blog.csdn.net/qq_37706228/article/details/83893772