您的位置:首页 > 理论基础 > 数据结构算法

面试复习-------算法与数据结构------链表

2017-08-24 17:28 429 查看
如果一个操作可能会改变头指针,那么参数应该是ListNode** phead

头插法和尾插法是指在建立单链表的时候,时间复杂度都是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将其删除。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: