您的位置:首页 > 其它

链表问题合集

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.轻松搞定面试中的链表题目
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: