链表面试题精讲

本篇博客是关于七月算法 ,曹鹏老师所讲的关于链表面试题部分的习题,加上自己的补充与笔记!

链表简介

链表:一个元素和下一个元素靠指针连接(松散),不能O(1)直接访问到第k个元素

n 单(向)链表 :只能找到下一个节点

n 双(向)链表:能找到上一个和下一个节点

n 循环(单、双)链表:首尾相接 形成环

Java : LinkedList

C++ : STL list

C : 指针

 

 

1 在单链表里插入/删除一个节点

插入

哪些指针要修改?前驱的next,新节点的next

我们要找到插入之前的那个节点

特殊情况: 在head之前插入(包括head == NULL)

  now->next = head;
  head = now;
//一般情况:在pre后面插入
  now->next = pre->next;
  pre->next = now;

 

删除

哪些指针要修改?前驱的next

我们要找到删除之前的那个节点

特殊情况? 删除head

temp =head->next;
delete head;
head = temp;
//一般情况,在pre后面删除
temp =pre->next;
pre->next =temp->next;
delete temp;


 

思考题

(1)双向链表的插入、删除

(2)循环有序链表的插入、删除 (建议断开、再连上)

(3)“懒”删除

1)要删除now这个节点 (不是最后一个)

2)把now复制成now->next

now->x = now->next->x

3)删除now->next

 

2 单链表翻转

思路: 把当前节点拿过来作为已经翻转结果的表头 (堆栈类似)

ListNode *result =0;
while (head) {
       temp = head->next;   //保存下一个节点
      head->next = result;   //当前节点放到结果的开头
      result = head;              //当前节点的头
      head = temp;      //head指向下一个节点
}
return result;
 


思考题

1翻转部分链表 (Leetcode 92)

(1)如何找到第m个元素和第n个元素

(2)如何处理前面和后面?

1)保存前面部分最后一个元素

2)保存后面部分第一个元素

3)特殊情况?


2每k个元素翻转一次 (Leetcode 25)

(1)前面翻好的部分 (小链表)

(2)要翻转的部分(K个)

(3)后面没处理的部分(小链表)

(4)不足k个怎么办


3 单链表里是否有环?如果有起点是哪里?环长度是多大?  (最后一个节点next不是空,而是前面某个节点) (Leetcode 141, 142)

方法1用一个set存放每个节点地址

注意: set存放的元素必须“有序”,而地址都是“整数”

set<ListNode*>have;
for (; head; head= head->next) {
         if (have.find(head) != have.end())return true;
               have.insert(head);
}
return false;
 


方法2不用set?

(1)用两个指针p1和p2, p1每次走一步,p2每次走两步,如果有圈一定会相遇

(2)为什么一定会相遇?

(3)相遇时如何找交点?

(4)一些变量:圈长n、起点到圈的起点距离a、p1到圈起点时,p2在圈中的位置(0<= x < n)。

 

分析:p1到起点后,经过n – x步相遇 (追及问题)

设相遇点到圈起点距离是b,p1走的距离是a + b。P2走的距离是a + b + k * n == 2*(a + b),从而a + b = k * n。

如何找圈的起点?

我们把p1拉回起点,p2从相遇点继续走。a步后,p1到圈起点,p2刚好也到圈起点!

如何找圈长?

相遇后,p2再走一圈并统计长度就是圈长

class solution {
         public:
                   ListNode*detectCycle(ListNode *head) {
                            ListNode*p1=head,*p2=head;
                            do {
                                     if((p2==0)|| (p2->next==0)) {
                                               return0;
                                     }
                                     p2=p2->next->next;
                                     p1=p1->next;
                            }while(p1!=p2);
                            for(p1=head;p1!=p2;p1=p1->next,p2=p2->next)
                            ;
                            return p1;
                   }
};
 


4 单向链表找交点 (Leetcode 160)

方法1: set记录一个链表里所有的节点

方法2: 一个链表长x,另外一个链表长y,(x >= y),第一个链表先走x – y步,再一起走……

class solution {
public:
         int getLength(ListNode *head) {
                   int r=0;
                   for(;head;head=head->next,++r)
                   ;
                   return r;
         }
        
         ListNode *getIntersectionNode(ListNode*headA,ListNode *headB) {
                   intlenA=getLength(headA),intB=getLength(headB);
                   if(lenA >=lenB) {
                            for(inti=lenA-lenB;i;--i,head=head->next)
                            ;
                   }
                   else {
                            for(inti=lenB-lenA;i;--i,headB=headB->next)
                            ;
                   }
                   for(;headA && headB&&headA !=headB;headA=headA->next,headB=headB->next)
                   ;
                   return(headA==headB)?headA:0;
         }
};


 

5 一个单链表除了next指针外还有一个random指针随机指向任何一个元素(可能为空)请复制它 (Leetcode138)

难点:我们不知道random指针在复制后链表的地址——复制元素地址变了

方法1map<旧地址,新地址>,

先按照普通方法复制链表,再两个链表同时走复制random(旧节点a,新节点a’)

a’ ->random =map[a->random]  (空单独处理)

 

方法2不用map

(1)插入:每个旧节点后面插入一个自身的“复本”

(2)复制random指针

一个旧节点a的复本是a->next;

a->random的复本是a->random->next;

新节点的random指针a->next->random = a->random->next (空值单独判断);

(3)拆分

1)旧节点链表是奇数项;

2)新节点链表是偶数项;

class solution {
public:
         RandomListNode*copyRandomList(RandomListNode *head) {
                   if(head==0) {
                            return 0;
                   }
                   for(RandomListNode*now=head;now;) {
                            RandomListNode*copy=new RandomListNode(now->label);
                            copy->next=now->next;
                            now->next=copy;
                            now=copy->next;
                   }
                  
                   for(RandomListNode*now=head;now;now=now->next->next) {
                            now->next->random=(now->random== 0)?0:now->random->next;
                   }
                  
                   RandomListNode*h=head->next,*t=h,*tail=head;
                   for(;;) {
                            tail=tail->next=t->next;
                            if(tail==0) {
                                     break;
                            }
                            t=t->next=tail->next;
                   }
                   return h;
         }
        
};


6 链表partition

链表里存放整数,给定x把比x小的节点放到>=x之前(Leetcode 86)

class solution {
public:
         ListNode *partition(ListNode* head,intx) {
                   ListNode*h1=0,*t1=0,*h2=0,*t2=0;
                   for(;head;head=head->next){
                            if(head->val <x) {
                                     if(t1) {
                                               t1=t1->next=head;
                                     }
                                     else {
                                               h1=t1=head;
                                     }
                            }
                            else if(t2) {
                                     t2=t2->next=head;
                            }
                            else {
                                     h2=t2=head;
                            }
                   }
                   if(t2) {
                            t2->next=0;
                   }
                   if(t1) {
                            t1->next=h2;
                   }
                   return h1?h1:h2;
         }       
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值