链表问题合集
2017-09-06 17:44
141 查看
一.前言
链表是一种常见的基础数据结构,也是面试笔试中常常碰到的题目.使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
由于不必按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
二.链表结构
class ListNode{ int value; ListNode next; public ListNode(int value){ this.value = value; this.next = null; } }
三.常见链表问题
1. 从尾到头打印链表; (递归和非递归)2. 反转链表(递归和非递归)
3. 在O(1)时间内,删除指定节点
4. 判断两个链表是否相交
5. 两个链表相交的第一个公共节点
6. 判断链表是否有环
7. 假设链表有环,找出入环的第一个节点
8. 合并两个有序链表
9. 链表升序排序
10. 链表的和
11. 求单链表节点的个数
12. 查找链表倒数第K个节点
13. 查找链表的中间节点
四.题目
1. 从尾到头打印链表
1.1 思路遍历和递归. 遍历可借助栈的特点,先进后出.或者链表反转后再打印.
1.2 代码
遍历:
void printFromTail(ListNode pHead){ if(pHead == null){ return; } Stack<ListNode> list = new Stack<>(); while(pHead != null){ list.push(pHead); pHead = pHead.next; } while(!list.isEmpty){ ListNode pNode = list.pop(); System.out.print(pNode.value+" "); } }
递归
void printFromTailRecursively(ListNode pHead){ if(pHead == null){ return; }else{ printFromTailRecursively(pHead.next); System.out.print(pNode.value+" "); } }
2.反转链表
2.1 思路递归和非递归.
2.2 代码
1.递归
ListNode reverseRecursively(ListNode pHead){ if (pHead == null || pHead.next == null){ return pHead; } List newHead = reverseRecursively(pHead.next); pHead.next.next = pHead; pHead.next = null; return newHead; }
2.非递归
ListNode reverse(ListNode pHead){ if(pHead == null || pHead.next = null){ return pHead; } ListNode pReversedHead = null; ListNode pCurrent = pHead; while(pCurrent!=null){ ListNode pTemp = pCurrent; pCurrent = pCurrent.next; pTemp.next = pReversedHead; pReversedHead = pTemp; } return pReversedHead; }
3.在O(1)时间内,删除指定节点
3.1 思路找到删除节点的下一节点,交换它们的值,并删除该节点.
3.2 代码
void deleteNode(ListNode pHead, ListNode pTOBeDeleted) { if (pHead == null || pTOBeDeleted == null) { return; } if (pTOBeDeleted.next != null) { ListNode pNode = pTOBeDeleted.next; pTOBeDeleted.value = pNode.value; pTOBeDeleted.next = pNode.next; pNode = null; }else { if (pTOBeDeleted == pHead) { pHead = null; pTOBeDeleted = null; return; }else { ListNode pNode = pHead; while(pNode.next!=pTOBeDeleted){ pNode = pNode.next; } pNode.next = null; } }
4. 判断两个链表是否相交
4.1 思路如果两个链表相交,则最后一个节点肯定相同
4.2 代码
boolean isCross(ListNode pHead1, ListNode pHead2) { if (pHead1 == null || pHead2 == null) { return false; } ListNode pNode1 = pHead1; while(pNode1.next != null){ pNode1 = pNode1.next; } ListNode pNode2 = pHead2; while(pNode2.next != null){ pNode2 = pNode2.next; } return pNode1==pNode2; }
5.两个链表相交的第一个公共节点
5.1 思路两个指针,链表长的指针先走diff(链表长度差)步,然后一起走,第一个相同点即交点.
5.2 代码
ListNode getFirstCrossNode(ListNode pHead1, ListNode pHead2) { if (pHead1 == null || pHead2 == null) { return null; } int len1 = 0; ListNode pNode1 = pHead1; while(pNode1.next !=null){ len1 ++; pNode1 = pNode1.next; } int len2 = 0; ListNode pNode2 = pHead2; while(pNode2.next !=null){ len2 ++; pNode2 = pNode2.next; } if (pNode1 != pNode2) { return null; } int diff = 0; if (len1>len2) { diff = len1-len2; pNode1 = pHead1; while (diff>0){ pNode1 = pNode1.next; diff --; } }else { diff = len2-len1; pNode2 = pHead2; while (diff>0){ pNode2 = pNode2.next; diff --; } } while(pNode1 != pNode2){ pNode1 = pNode1.next; pNode2 = pNode2.next; } return pNode1; }
6. 判断链表是否有环
6.1 思路两个指针,一个走两步,一个走一步,若相遇则必定成环.
6.2 代码
boolean hasCircle(ListNode pHead) { if (pHead == null) { return false; } ListNode pFast = pHead; ListNode pSlow = pHead; while(pFast!=null && pFast.next!=null){ pFast = pFast.next.next; pSlow = pSlow.next; if (pFast==pSlow) { return true; } } return false; }
7.假设链表有环,找出入环的第一个节点
7.1 思路假想从环内断开,则第一个节点即为两个单链表的第一个公共节点
7.2 代码
ListNode getFirstNodeInCircle(ListNode pHead) { if (pHead == null || pHead.next == null) { return null; } ListNode pFast = pHead; ListNode pSlow = pHead; while (pFast != null && pFast.next != null){ pSlow = pSlow.next; pFast = pFast.next.next; if (pSlow == pFast) { //节点相遇 break; } } if (pFast == null || pFast.next == null) { return null; } ListNode pAssumedTail1 = pSlow; //pAssumedTail 环中节点,假设从这断开 ListNode pHead1 = pHead; ListNode pHead2 = pAssumedTail1.next; ListNode pNode1 = pHead1; int len1 = 1; while (pNode1 != pAssumedTail1){ pNode1 = pNode1.next; len1++; } ListNode pNode2 = pHead2; int len2 = 1; while (pNode2 != pAssumedTail1){ pNode2 = pNode2.next; len2++; } pNode1 = pHead1; pNode2 = pHead2; if (len1>len2) { int gap = len1 - len2; while(gap>0){ pNode1 = pNode1.next; gap--; } }else { int gap = len2 - len1; while (gap>0){ pNode2 = pNode2.next; gap--; } } while (pNode1!=pNode2){ pNode1 = pNode1.next; pNode2 = pNode2.next; } return pNode1; }
8. 合并两个有序链表
8.1 思路两个指针扫描,比较,将最小节点接入合并的链表中.
8.2 代码
1.非递归
ListNode merge(ListNode pHead1, ListNode pHead2) { if (pHead1 == null) { return pHead2; } if (pHead2 == null) { return pHead1; } ListNode pMerge = null; if (pHead1.value<pHead2.value) { pMerge = pHead1; pHead1 = pHead1.next; pMerge.next = null; //58,59顺序反,则phead1为空 }else { pMerge = pHead2; pHead2 = pHead2.next; pMerge.next = null; } ListNode pTemp = pMerge; //最xin添加的节点 while (pHead1 !=null && pHead2!=null) { if (pHead1.value < pHead2.value) { pTemp.next = pHead1; pHead1 = pHead1.next; pTemp = pTemp.next; pTemp.next = null; //解除最后一个节点的指向,可以省略 }else { pTemp.next = pHead2; pHead2 = pHead2.next; pTemp = pTemp.next; pTemp.next = null; } } while (pHead1 != null) { pTemp.next = pHead1; pHead1 = pHead1.next; } while (pHead2 !=null) { pTemp.next = pHead2; pHead2 = pHead2.next; } return pMerge; }
2.递归
ListNode mergeRecursively(ListNode pHead1, ListNode pHead2) { if (pHead1 == null) { return pHead2; } if (pHead2 == null) { return pHead1; } ListNode pMergeHead = null; if (pHead1.value<pHead2.value) { pMergeHead = pHead1; pMergeHead.next = mergeRecursively(pHead1.next, pHead2); }else { pMergeHead = pHead2; pMergeHead.next = mergeRecursively(pHead1, pHead2.next); } return pMergeHead; }
9. 链表升序排序
9.1 思路冒泡法,比较相邻两个链表的值
9.2 代码
ListNode listSort(ListNode pHead) { if (pHead == null || pHead.next == null) { return pHead; } ListNode tail = null; ListNode cur = null; cur = pHead; while(cur!=tail){ while(cur.next!=tail){ if (cur.value>cur.next.value) { int temp = cur.value; cur.value = cur.next.value; cur.next.value = temp; } cur = cur.next; } tail = cur; cur = pHead; //重新回到起点 } return pHead; }
10. 链表的和
10.1 思路递归
10.2 代码
int listSum(ListNode pHead){ if(pHead == null){ return 0; } return pHead.value+listSum(pHead.next); }
11. 求单链表节点的个数
11.1 思路递归
11.2 代码
int listNodeNum(ListNode pHead){ if(pHead == null){ return 0; } return 1+listNodeNum(pHead.next); }
12. 查找链表倒数第K个节点
12.1 思路两个指针,一个先走K步
12.2 代码
ListNode getLastKthNode(ListNode pHead,int k) { if (pHead == null || k<=0) { return null; } ListNode pAhead = pHead; ListNode pBehind = pHead; while(k>1 && pAhead!=null){ pAhead = pAhead.next; k--; } if (pAhead == null) { return null; } while(pAhead.next != null){ pAhead = pAhead.next; pBehind = pBehind.next; } return pBehind; }
13.查找链表的中间节点
13.1 思路思路和上题类似,两个指针,一个一次走两步,一个一次走一步
13.2 代码
ListNode getMiddleNode(ListNode pHead) { if (pHead == null || pHead.next == null) { return pHead; } ListNode pAhead = pHead; ListNode pBehind = pHead; while(pAhead.next != null){ pAhead = pAhead.next; pBehind = pBehind.next; if (pAhead.next!=null) { pAhead = pAhead.next; } } return pBehind; }
完)
码了一天,有不当之处,麻烦指出.
参考文献
1.链表2.链表反转
3.轻松搞定面试中的链表题目
相关文章推荐
- Leetcode Linked List Problem 链表问题合集
- 判断链表中是否有环 ----- 有关单链表中环的问题
- 链表有环和相交问题
- 循环链表中约瑟夫环的问题
- 单链表的环相关问题
- 实验一:[链表操作]约瑟夫斯问题求解
- 环形链表解决约瑟夫游戏问题
- 转:经典面试题:链表的相交与环问题
- 问题 C: 瑞神要考研(重排链表)
- 安装ubuntu14.04问题合集
- 链表实现孩子出队列问题
- 单循环链表 解决约瑟夫环的问题
- 关于链表的问题
- 关于两条链表交叉(交叉有环)的问题解决
- 《编程之美》读书笔记(十):“链表相交”扩展问题
- 数据结构与算法分析笔记与总结(java实现)--链表16:单链表判断是否有环问题
- 当年所谓不解决的动态链表问题,再次回顾,呵呵
- 数据结构学习的简单问题(三):用链表实现多项式相加
- Tomcat JDK 部署及错误问题合集
- 线性表---单循环链表(约瑟夫环问题)