面试复习-------算法与数据结构------链表
2017-08-24 17:28
429 查看
如果一个操作可能会改变头指针,那么参数应该是ListNode** phead
头插法和尾插法是指在建立单链表的时候,时间复杂度都是O(n)
将节点添加到一个已存在链表的尾部也是O(n),那么为何尾插法可以O(n)而不是O(n^2),原因是在创建的时候会维护一个链表尾部的指针。所以不用每次都循环寻找链表尾节点。
(1)反转链表(剑指offer16)
为了防止断链需要设置三个指针分别指向pPrev、pNode、pNext
(2)O(1)时间删除链表节点(剑指offer13)
思路:将该节点的值换成后一个节点的值,再将后一个节点删除;有点取巧
如果是尾节点,则还是得花费O(n)
(3)链表中倒数第k个节点(剑指offer15)
快慢指针的使用,一个指针先走K步,然后两个指针一起走直到第一个指针到达尾部;需要考虑链表不足K的情况
(附:在做链表题的时候(或者说涉及到指针的时候),一定考虑鲁棒性;即对于一个参数pNode一定要判断:(pNode == NULL); 要使用pNode->NEXT的时候,一定要判断(pNode->NEXT == NULL))
类似的快慢指针的题还有:
--------求链表的中间节点(快慢指针,一个一步每次一个两步每次);
--------判断单链表是否存在环(快慢指针,判断他们是否会相遇);
(4)公共节点问题:利用特性,公共节点之后的节点都是公共节点,即只能为Y型
------判断两个链表是否相交;直接可以看这两个链表的尾节点是否相同
------求两个链表的第一个公共节点(剑指offer37)首先获取两个链表的长度,然后算出长度差x,令一个指针先走x步,之后两个指针一起走,直至两个指针指向的节点相同
(5)合并两个已排序的链表(剑指offer17)
递归的代码比较简洁,非递归代码如下:
(6)复杂链表的复制(剑指offer26)
在原始链表的每一个节点N后面创建一个N'节点,复制值和指针,最后再将该链表分解成两个链表。
(7)左旋转链表
类似于左旋转字符串,即将链表的最右边K个节点移到链表头指针之前;
由于k可能大于链表长度,可以先遍历链表统计长度并获得尾指针,然后旋转k%length个节点即可;
(8)技巧:有时候遍历链表的时候,pPre不知道从何处初始化(可能要手动处理一个使pNode = phead->next),有的时候可能会涉及到删除pHead节点;这个时候可以手动构造一个节点放在头结点之前,最后返回该节点的next指针域即可。例:删除排序链表中值相同的节点(leetcode82):
ListNode* deleteDuplicates(ListNode* head) {
if(head == NULL)return NULL;
ListNode*
4000
pHead = new ListNode(-1);
pHead->next = head;
ListNode* pPre = pHead;
ListNode* pNode = head;
while(pNode!=NULL){
while(pNode->next!=NULL && pNode->val == pNode->next->val)pNode = pNode->next;
if(pPre->next != pNode){
pPre->next = pNode->next;
pNode = pNode->next;
}else{
pPre = pNode;
pNode = pNode->next;
}
}
ListNode* result = pHead->next;
delete pHead;
return result;
}手动构造了一个pHead,这个时候就可以使pPre = pHead了。注意最后要用delete将其删除。
头插法和尾插法是指在建立单链表的时候,时间复杂度都是O(n)
将节点添加到一个已存在链表的尾部也是O(n),那么为何尾插法可以O(n)而不是O(n^2),原因是在创建的时候会维护一个链表尾部的指针。所以不用每次都循环寻找链表尾节点。
(1)反转链表(剑指offer16)
为了防止断链需要设置三个指针分别指向pPrev、pNode、pNext
ListNode* reverse(ListNode* phead) { if(phead==NULL) return NULL; ListNode* pre; ListNode* index; ListNode* pnext; ListNode* result; index=phead; pre=NULL; while(index!=NULL) { pnext=index->next; if(pnext==NULL) { result=index; } index->next=pre; pre=index; index=pnext; } return result; }
(2)O(1)时间删除链表节点(剑指offer13)
思路:将该节点的值换成后一个节点的值,再将后一个节点删除;有点取巧
如果是尾节点,则还是得花费O(n)
(3)链表中倒数第k个节点(剑指offer15)
快慢指针的使用,一个指针先走K步,然后两个指针一起走直到第一个指针到达尾部;需要考虑链表不足K的情况
(附:在做链表题的时候(或者说涉及到指针的时候),一定考虑鲁棒性;即对于一个参数pNode一定要判断:(pNode == NULL); 要使用pNode->NEXT的时候,一定要判断(pNode->NEXT == NULL))
ListNode* find_k(ListNode* phead,int k) { if(phead==NULL||k<=0) return NULL; ListNode* index1=phead; ListNode* index2=NULL; //先让一个指针到k-1个节点 for(int i=0;i<k-1;i++) { if(index1->next != NULL) index1=index1->next; else return NULL; } index2 = phead; while (index1->next!=NULL) { index1=index1->next; index2=index2->next; } return index2; }
类似的快慢指针的题还有:
--------求链表的中间节点(快慢指针,一个一步每次一个两步每次);
--------判断单链表是否存在环(快慢指针,判断他们是否会相遇);
(4)公共节点问题:利用特性,公共节点之后的节点都是公共节点,即只能为Y型
------判断两个链表是否相交;直接可以看这两个链表的尾节点是否相同
------求两个链表的第一个公共节点(剑指offer37)首先获取两个链表的长度,然后算出长度差x,令一个指针先走x步,之后两个指针一起走,直至两个指针指向的节点相同
int getListLength(ListNode* phead) { int length=0; ListNode* pnode=phead; while(pnode!=NULL) { pnode=pnode->next; ++length; } return length; } ListNode* FindFirstCommonNode(ListNode* phead1,ListNode* phead2) { int length1= getListLength(phead1); int length2= getListLength(phead2); int DifLength=(length1>length2)?(length1-length2):(length2-length1); ListNode* pnode1=phead1; ListNode* pnode2=phead2; if(length1>length2) for(int i=0;i<DifLength;++i) pnode1=pnode1->next; else if(length1<length2) for(int i=0;i<DifLength;++i) pnode2=pnode2->next; while(pnode1!=NULL && pnode2 != NULL && pnode1 != pnode2) { pnode1=pnode1->next; pnode2=pnode2->next; } return pnode1; }
(5)合并两个已排序的链表(剑指offer17)
递归的代码比较简洁,非递归代码如下:
ListNode* list_sort(ListNode* phead1,ListNode* phead2) { if(phead1 == NULL)return phead2; if(phead2 == NULL)return phead1; ListNode* result = NULL; ListNode* pNode1 = phead1; ListNode* pNode2 = phead2; if(phead1->key < phead2->key){ result = phead1; pNode1 = phead1->next; } else{ result = phead2; pNode2 = phead2->next; } ListNode* pCurrent = result; while(pNode1 != NULL && pNode2 != NULL ){ if(pNode1->key < pNode2->key){ pCurrent->next = pNode1; pNode1 = pNode1->next; }else{ pCurrent->next = pNode2; pNode2 = pNode2->next; } pCurrent = pCurrent->next; } if(pNode1 != NULL){ pCurrent->next = pNode1; } if(pNode2 != NULL){ pCurrent->next = pNode2; } return result; }两个链表合成一个链表的常用技巧就是使用一个pCurrent指针指向当前节点,每次只需添加到pCurrent后面,并且pCurrent后移一位即可。
(6)复杂链表的复制(剑指offer26)
在原始链表的每一个节点N后面创建一个N'节点,复制值和指针,最后再将该链表分解成两个链表。
(7)左旋转链表
类似于左旋转字符串,即将链表的最右边K个节点移到链表头指针之前;
由于k可能大于链表长度,可以先遍历链表统计长度并获得尾指针,然后旋转k%length个节点即可;
ListNode* rotateRight(ListNode* head, int k) { if(head == NULL)return head; int length = 1; ListNode* pNode = head; while(pNode->next != NULL){ pNode = pNode->next; ++length; } pNode->next = head; k = k % length; //真正需要转移的个数,包含了k>length的情况 if(k !=0){ for(int i = 0; i < length - k; ++i){ pNode = pNode->next; } } ListNode* pNext = pNode->next; pNode->next = NULL; return pNext; }
(8)技巧:有时候遍历链表的时候,pPre不知道从何处初始化(可能要手动处理一个使pNode = phead->next),有的时候可能会涉及到删除pHead节点;这个时候可以手动构造一个节点放在头结点之前,最后返回该节点的next指针域即可。例:删除排序链表中值相同的节点(leetcode82):
ListNode* deleteDuplicates(ListNode* head) {
if(head == NULL)return NULL;
ListNode*
4000
pHead = new ListNode(-1);
pHead->next = head;
ListNode* pPre = pHead;
ListNode* pNode = head;
while(pNode!=NULL){
while(pNode->next!=NULL && pNode->val == pNode->next->val)pNode = pNode->next;
if(pPre->next != pNode){
pPre->next = pNode->next;
pNode = pNode->next;
}else{
pPre = pNode;
pNode = pNode->next;
}
}
ListNode* result = pHead->next;
delete pHead;
return result;
}手动构造了一个pHead,这个时候就可以使pPre = pHead了。注意最后要用delete将其删除。
相关文章推荐
- 面试复习-------算法与数据结构------字符串
- 一步一步复习数据结构和算法基础-链表(1)
- 面试复习-------算法与数据结构------二分搜索
- 数据结构 - 链表 - 面试中常见的链表算法题
- 微软等数据结构+算法面试100题(3)--怎样把一个链表掉个顺序(也就是反序,注意链表的边界条件并考虑空链表)?
- 微软等数据结构+算法面试100题(43)-- 把二元查找树转变成排序的双向链表
- 一步一步复习数据结构和算法基础-链表(3)
- 面试复习重点 算法 数据结构 【山科大牛陈磊整理】
- 微软等数据结构+算法面试100题(19)--链表
- 微软等数据结构+算法面试100题(31)--在O(1)时间内删除链表结点
- 2018.01.08大数据面试题-算法大全-数据结构-算法1-单链表反转
- 微软等数据结构+算法面试100题(32)--从尾到头输出链表
- 面试复习-------算法与数据结构------算法设计
- 面试复习-------算法与数据结构------排序
- 面试复习-------算法与数据结构------二叉树
- 一步一步复习数据结构和算法基础-链表(2)
- 数据结构 算法面试100题 之 二叉树转换成双向链表
- 算法数据结构复习[单链表]
- 面试复习重点 算法 数据结构 【山科大牛陈磊整理】
- 【郝斌数据结构自学笔记】27-29_链表插入和删除算法的演示_复习