美文网首页
剑指offer-链表

剑指offer-链表

作者: Catherin_gao | 来源:发表于2020-12-04 13:19 被阅读0次

一.链表的基础结构

  class ListNode {
      int val;
      ListNode *next;
      ListNode(int x) : val(x), next(NULL) {}
  };

1.1 尾部添加节点

void AddNode(ListNode** head, int value){            //ListNode* head为啥不行
    ListNode* new_node=new ListNode(value);               //先new一个新节点

    if (*head == NULL){
       *head=new_node;
    }else{
       ListNode* tmp=*head;
       while(tmp->next!=NULL){
           tmp=tmp->next;
       }
       tmp->next=new_node;
   }
}

二. Easy 题目

2.1 剑指 Offer 06. 从尾到头打印链表

  • 不能改变链表的结构
  • 倒序想到递归

2.1.1 栈

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> result;
        if(head ==NULL)
            return result;

        stack<int> tmp;
        while(head!=NULL){
            tmp.push(head->val);
            head=head->next;
        }
        while(tmp.size()){
            result.push_back(tmp.top());
            tmp.pop();
        }
        return result;
    }
};
注意
  • 返回值有类型的话,不能返回NULL。

2.1.2 递归

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> result;
        if(head==NULL)
            return result;
        reverseList(head,result);
        return result;
    }

    void reverseList(ListNode* head, vector<int> & result) {
        if(head==NULL)
            return;
        if(head->next!=NULL){
            reverseList(head->next,result);
        }
        result.push_back(head->val);
    }
};
注意
  • 递归可能导致函数调用栈溢出,栈的鲁棒性更好些。
  • 错误解法:对递归概念不够熟悉。
        if(head->next!=NULL){
            reverseList(head->next,result);
        }else{
        result.push_back(head->val);
       }

2.2 剑指 Offer 22. 链表中倒数第k个节点

类似题目: 面试题 02.02. 返回倒数第 k 个节点

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        if(head ==NULL || k==0) return NULL;            //k==0一开始没想到,注意
        ListNode* pre=head;
        while(--k){
            if(pre->next != NULL){
                pre=pre->next;
            }else{
                return NULL;
            }
        }
        ListNode* aft=head;
        while(pre->next!=NULL){
            pre=pre->next;
            aft=aft->next;
        }
        return aft;
    }
};
注意
  • 习惯于k++, k值已知时,k--更优。k--和--k注意区分。
  • 需要增加k是否等于0的判断。

2.3 剑指 Offer 24. 反转链表

  • 做了很多次反转链表的题,但是没有详细的记下思路。
  • 反转顺序:先按照pre, node, next获取最初状态。然后以中间节点node为基准,改变指针方向。最后再以pre, node, next的顺序获取新的节点。
  • node不为空就可以继续翻转。
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL || head->next ==NULL)
            return NULL;
        ListNode* pre=NULL;
        ListNode* node=head;
        ListNode* new_head=NULL;

        while(node!=NULL){
            ListNode* after=node->next;
            if(after ==NULL)
               new_head=node;

           node->next=pre;

           pre=node;
           node=after;
        }
        return new_head;
    }
};

2.4 21. 合并两个有序链表

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL)
           return l2;
        if(l2 ==NULL)
           return l1;
        ListNode* new_head=new ListNode();
        if(l1->val < l2->val){
          new_head->val=l1->val;
          l1=l1->next;
        }else{
          new_head->val=l2->val;
          l2=l2->next;            
        }
        ListNode* node=new_head;
        while(l1!=NULL && l2 !=NULL){
            if(l1->val < l2->val){
                node->next=new ListNode(l1->val);
                node=node->next;
                l1=l1->next;
            }else{
                node->next=new ListNode(l2->val);
                node=node->next;
                l2=l2->next;            
            }
        }
        node->next=l1!=NULL? l1:l2;
        return new_head;
    }
};

注意:更高效版本

  • 可以设置一个假的头结点,将头结点操作和后面节点的操作结合起来。
  • 判断l1, l2是否为空,使用break的话更高效。
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* fake_head=new ListNode(-1);
        ListNode* node=fake_head;
        while(l1!=NULL && l2 !=NULL){
            if(l1->val < l2->val){
                node->next=new ListNode(l1->val);                
                l1=l1->next;
            }else{
                node->next=new ListNode(l2->val);
                l2=l2->next;            
            }
            node=node->next;                              //相同代码部分提出来,更高效。
        }
        node->next=l1!=NULL? l1:l2;
        return fake_head->next;
    }
};

还可以使用递归方法

三. Medium题目

3.1 剑指 Offer 35. 复杂链表的复制

  • 第一种方法先复制一遍链表,对着原来的列表复制指向信息,每个列表结点都需要遍历寻找,O(n^2).
  • 第二种 用哈希表实现节点遍历。空间换时间能降到O(n)。
  • 第三种 不用辅助空间的情况下,O(n)时间复杂度。
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/
class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == NULL) 
          return NULL;
        copyList(head);
        copyRandom(head);
        return splitList(head);
    }

    void copyList(Node* head) {
        Node* node=head;
        while(node){
            Node* new_node=new Node(node->val);
            new_node->next=node->next; 
            node->next=new_node;
            node=new_node->next;
        }
    }

    void copyRandom(Node* head) {
        while(head){
            if(head->random)
                head->next->random=head->random->next;
            head=head->next->next;
        }
    }

    Node* splitList(Node* head){
        Node* new_head=head->next;
        
        while(head!=NULL){
            Node* next_node=head->next;
            if(next_node)
                head->next=next_node->next;
            else
                head->next=NULL;
            head=next_node;
        }
        return new_head;
    }
};

相关文章

网友评论

      本文标题:剑指offer-链表

      本文链接:https://www.haomeiwen.com/subject/mnxgwktx.html