链表 leetcode题目总结 c++

链表

链表和数组最大的区别在于,链表不支持随机访问,不像数组可以对任意一位的数据进行访问,链表只能从头一个一个往下访问,寻找下一个元素,像穿针引线似的。也正因为链表的这种特点,增大了链表题目的难度。

本文主要讨论的是单链表,单链表中的链表结点结构体定义如下

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

由上面的代码可以看出,每个结点包含两个域,一个是val表示结点的值,一个是next指针,指向下一个结点。

1)做链表类题目的关键是画图,将链表指针指向的内存的变化用图表示出来。
2)链表类题目还有一个重要的技巧是设置虚拟头结点。在做题的时候,头结点(head)常常要特殊处理,因为head结点没有前一个结点,这就决定了头节点和后续节点的处理方法不同。但是如果在头结点前加了一个虚拟头节点,那它就可以用正常的方法来处理了。

虚拟头节点定义如下:

ListNode* dummyHead = new ListNode(0); //新建一个结点,value为0
dummyHead->next = head; //让它next指针指向头结点
虚拟头结点
相关题目
  • leetcode #21. Merge Two Sorted Lists 合并两个链表
  • leetcode #206. Reverse Linked List 翻转链表
  • leetcode #92. Reverse Linked List II 翻转链表中指定区间的结点
  • leetcode #83. Remove Duplicates from Sorted List 删除链表中重复元素
  • leetcode #2. Add Two Numbers 将两个链表转化成整数并相加,再存入链表中
  • leetcode #445. Add Two Numbers II** 和上题类似,但更难一些
  • leetcode #237. Delete Node in a Linked ListDelete Node in a Linked List 删除指定结点
  • leetcode #328. Odd Even Linked List 奇偶链表
  • leetcode #19. Remove Nth Node From End of List 删除倒数第n个结点

leetcode #21. Merge Two Sorted Lists 合并两个链表
题目:Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
Example:
Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL) return l2;
        if(l2 == NULL) return l1;
        if(l1->val > l2->val){
            ListNode *tmp = l2;
            tmp->next = mergeTwoLists(l1, l2->next);
            return tmp;
        }
        else{
            ListNode *tmp = l1;
            tmp->next = mergeTwoLists(l1->next, l2);
            return tmp;
        }
    }
};

leetcode #206. Reverse Linked List 反转列表
思路:一般链表的题目都会约束不能对链表的值进行操作,只能对链表的结点进行操作。处理链表问题,需要多用几个指针来存储结点信息。

leetcode206

在链表中,一旦访问链表中的某一个域,一定要保证这个域不为空,为了避免这个问题,可以在刚开始的判断一下该链表是否为空。

Solution:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur = head;
        ListNode* pre = NULL;
        while(cur!= NULL){
            ListNode* next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }

    <span class="hljs-keyword">return</span> pre;
}

};

leetcode #92. Reverse Linked List II 翻转链表中指定区间的结点
一定要画图!题目要求one-pass,所以遍历的时候要保存四个结点:m前一个node, 第m个node, 第n个node,第n+1个node;

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        if (head == NULL or head->next == NULL)
            return head;
    ListNode *dummyHead = <span class="hljs-keyword">new</span> ListNode(<span class="hljs-number">0</span>);
    dummyHead-&gt;next = head;
    ListNode *pre = dummyHead;
    ListNode *cur = head;
    int pos = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">while</span> (pos &lt; m) {
        pre = cur;
        cur = cur-&gt;next;
        ++pos;
    }
    ListNode *preNode = pre; <span class="hljs-comment">//m-1 node</span>
    ListNode *curNode = cur; <span class="hljs-comment">//m node</span>
    
    <span class="hljs-comment">// cur -&gt; Node_m</span>
    <span class="hljs-keyword">while</span> (pos &lt;= n) {
        ListNode *next = cur-&gt;next; 
        cur-&gt;next = pre;
        pre = cur;
        cur = next;
        ++pos;
    }
    preNode-&gt;next = pre; <span class="hljs-comment">// m - 1 -&gt; n</span>
    curNode-&gt;next = cur; <span class="hljs-comment">// m -&gt; n + 1</span>
    <span class="hljs-keyword">return</span> dummyHead-&gt;next;
}  

};

leetcode #83. Remove Duplicates from Sorted List 删除链表中重复元素
无非就是在遍历的时候,用三个指针来保存结点:previous, current, next;

class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
    <span class="hljs-keyword">if</span>(head == <span class="hljs-keyword">NULL</span> || head-&gt;next == <span class="hljs-keyword">NULL</span>) <span class="hljs-keyword">return</span> head;
    ListNode* pre = head;
    ListNode* cur = pre-&gt;next;
    
    <span class="hljs-keyword">while</span>(cur != <span class="hljs-keyword">NULL</span>)
    {
        <span class="hljs-keyword">if</span>(cur-&gt;val == pre-&gt;val)
        {
            
            <span class="hljs-keyword">if</span>(cur-&gt;next == <span class="hljs-keyword">NULL</span>)
            {
                
                pre-&gt;next = cur-&gt;next;
                <span class="hljs-keyword">return</span> head;
            }
            <span class="hljs-keyword">else</span>
            {
                cur = cur-&gt;next;
                pre-&gt;next = cur;
            }
        }
        <span class="hljs-keyword">else</span>
        {
            pre = cur;
            cur = cur-&gt;next;
        }
    }
    <span class="hljs-keyword">return</span> head;
}

};

leetcode #2. Add Two Numbers 相加,再存入链表中
这题不能用一个整数来保存然后再相加,因为会出现溢出的情况;下面的答案是逐位相加,将进位保存

class Solution {
public:
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        ListNode *head = new ListNode(0);
        ListNode *cur = head;
    int extra = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">while</span>(l1 || l2 || extra)
    {
        int sum = (l1?l1-&gt;val:<span class="hljs-number">0</span>) + (l2?l2-&gt;val:<span class="hljs-number">0</span>) + extra;
        extra = sum/<span class="hljs-number">10</span>;
        l1 = l1?l1-&gt;next:l1;
        l2 = l2?l2-&gt;next:l2;
        cur-&gt;next = <span class="hljs-keyword">new</span> ListNode(sum%<span class="hljs-number">10</span>);
        cur = cur-&gt;next;
        cout&lt;&lt;sum&lt;&lt;endl;
    }
    <span class="hljs-keyword">return</span> head-&gt;next;
}

};

leetcode #445. Add Two Numbers II
和#2题类似,但是难了很多。如果把链表翻转之后,就转换成了2,把加完之后的链表再翻转就是想要的答案了。所以该题是83和2的结合。

class Solution {
public:
    ListNode* reverse(ListNode* head)
    {
        ListNode* pre = NULL;
        ListNode* cur = head;
        if(cur == NULL) return head;
        while(cur != NULL) {
            ListNode* next = cur->next;
        cur-&gt;next = pre;
        pre = cur;
        cur = next;
    }
    <span class="hljs-keyword">return</span> pre;
}


ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
    l1 = reverse(l1);
    l2 = reverse(l2);
    
    ListNode head(<span class="hljs-number">0</span>);
    ListNode* cur = &amp;head;
    int extra = <span class="hljs-number">0</span>;
    
    <span class="hljs-keyword">while</span>(l1 || l2 || extra){
        
        int sum = (l1?l1-&gt;val:<span class="hljs-number">0</span> )+(l2?l2-&gt;val:<span class="hljs-number">0</span>) + extra;
        extra = sum/<span class="hljs-number">10</span>;
        l1 = l1?l1-&gt;next:<span class="hljs-number">0</span>;
        l2 = l2?l2-&gt;next:<span class="hljs-number">0</span>;
        cur-&gt;next = <span class="hljs-keyword">new</span> ListNode(sum%<span class="hljs-number">10</span>);
        cur = cur-&gt;next;
    }
    cur = reverse(head.next);
    <span class="hljs-keyword">return</span> cur;
}

};

leetcode #237. Delete Node in a Linked ListDelete Node in a Linked List 删除指定结点
注意本题是删除指定节点,而不是指定值
思路:由于指定节点前一个节点无法得到,所以就将下一个元素的值覆盖该节点,然后将下一个节点删除。一般情况下,我们对链表操作时,都不改变节点的值,只对节点本身操作。但是这题是特殊情况,所以链表问题不一定都是穿针引线的题
易错点:要考虑删除节点的下一个节点为空的情况,这种情况直接删掉该元素就可以了。

class Solution {
public:
    void deleteNode(ListNode* node) {
        if(node == NULL) return;
        if(node->next == NULL) {
            delete node;
            return;
        }
        node->val = node->next->val;
    ListNode* delNode = node-&gt;next;
    node-&gt;next = delNode-&gt;next;
    delete delNode;
    <span class="hljs-keyword">return</span>;
    
}

};

leetcode #328. Odd Even Linked List 奇偶链表

class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        if(head == NULL || head->next == NULL) return head;
        if(head->next->next == NULL) return head;
        ListNode* preOdd = head;
        ListNode* preEven = head->next;
        ListNode* odd = head->next->next;
    <span class="hljs-comment">//目前肯定有&gt;=3个节点</span>
    ListNode* even = head-&gt;next-&gt;next-&gt;next;
    preOdd-&gt;next = odd;
    odd-&gt;next = preEven;
    
    preEven-&gt;next = even;
    <span class="hljs-keyword">if</span>(preEven-&gt;next == <span class="hljs-keyword">NULL</span>){
        <span class="hljs-keyword">return</span> preOdd;
    }
    
    <span class="hljs-comment">//目前肯定有4个节点;</span>
    int i = <span class="hljs-number">1</span>;

    ListNode* cur = even-&gt;next; <span class="hljs-comment">//第5个节点</span>
    
    <span class="hljs-keyword">while</span>(cur != <span class="hljs-keyword">NULL</span>)
    {
        <span class="hljs-keyword">if</span>(i%<span class="hljs-number">2</span> != <span class="hljs-number">0</span>)
        {
            
            odd-&gt;next = cur;
            cur = cur-&gt;next;
            odd = odd-&gt;next;
            odd-&gt;next = preEven;
            
        }
        <span class="hljs-keyword">else</span>
        {
            even-&gt;next = cur;
            cur = cur-&gt;next;
            even = even-&gt;next;
            even-&gt;next = <span class="hljs-keyword">NULL</span>;
        }
        i++;
    }
    
    even-&gt;next = <span class="hljs-keyword">NULL</span>;
    <span class="hljs-keyword">return</span> preOdd;
}

};

leetcode #19. Remove Nth Node From End of List 删除倒数第n个结点
删除指定倒数第n个元素
思路:解法1)就是two-pass;第一遍遍历链表长度,第二遍删除元素
解法2)保存p节点和q节点,p和q之间对节点数刚好为n,相当于一个滑动窗口,不断往后移动,直到q节点移动到空节点时,这时p节点指向的节点时倒数n个节点的前一个节点
易错点:弄清题目中的n是从0开始还是从1开始算的;在这题中n是从1开始数,并且n是合法的

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
    ListNode* dummyHead = <span class="hljs-keyword">new</span> ListNode(<span class="hljs-number">0</span>);
    dummyHead-&gt;next = head;
    
    ListNode* p = dummyHead;
    ListNode* cur = head;
    <span class="hljs-keyword">while</span>(n--)
    {
        cur = cur-&gt;next;
    }
    ListNode* q = cur;
    <span class="hljs-keyword">while</span>(q != <span class="hljs-keyword">NULL</span>)
    {
        p = p-&gt;next;
        q = q-&gt;next;
    }
    
    ListNode* delNode = p-&gt;next;
    p-&gt;next = delNode-&gt;next;
    delete delNode;
    <span class="hljs-keyword">return</span> dummyHead-&gt;next;
}

};

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值