zoukankan      html  css  js  c++  java
  • 【算法篇】链表专题

      前言:

      从今天开始要攻克算法专题了,今天是链表篇,关于链表相关的考题,不会太多涉及时间复杂度,而主要考察链表和指针操作;为啥大厂喜欢考察数据结构和算法?因为这些是对基本功的升华,不会考察数组指针、函数指针等,考一个链表,就能考察对指针的理解,我相信不理解指针,链表学起来很费劲!

      一、简介

      我会一个模块一个模块进行学习和练习,练习时我会从leetcode上选题,都知道leetcode吧?是OJ中最权威的平台了,在上面可以找算法题和练习,很好的一个网站,每一个题都会说明leetcode的第几题,方便大家查找和练习。

      二、反转链表

      LeetCode上第206题:Reverse Linked List,官网是英文,但鉴于英文对一些人看起来比较费劲,翻译成中文,如下:

    反转单链表。
    例子:
    输入:1 - > 2 - > 3 - > 4 - > 5 - > NULL
    输出:5 - > 4 - > 3 - > 2 - > 1 - > NULL
    跟进:
    链表可以迭代或递归地反转。你能实现这两个吗?

      可能有人会想到直接去改链表节点里的值,这是不允许,一般都是操作next指针,去改变指针指向;画图进行讲解,如下:

       

      需要三个指针pre/cur/next去反转,将2位置指向pre位置,pre指向1号位置,1号位置指向3号位置,这样就可以进行反转了。代码如下:

    struct ListNode {
        int val;
        ListNode *next;
        ListNode(int x) : val(x), next(NULL) {}
    };
    
    // 206. Reverse Linked List
    // https://leetcode.com/problems/reverse-linked-list/description/
    // 时间复杂度: O(n)
    // 空间复杂度: O(1)
    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
    
            ListNode* pre = NULL;
            ListNode* cur = head;
            while(cur != NULL){
                ListNode* next = cur->next;
                cur->next = pre;
                pre = cur;
                cur = next;
            }
    
            return pre;
        }
    };

      我是按照LeetCode的格式进行编写的,然后去LeetCode上去试一下,鉴于可能有人不知道怎么使用LeetCode,我简单进行演示一下怎么使用:

      第一步:百度leetcode,如下:

      

      

        第二步:点击“Create Account”,创建自己的用户,需要填写邮箱,需要点击链接进行激活,否则刷题无法提交;刚开始写的邮箱没给我发邮件,又在个人资料里重新换了邮箱,就可以收到了;

        第三步:在首页找题,如果能记住题目,可以输入题目进行搜索;也可以搜索题号,如206,也可以搜索到,如下图:

     

      

    第四步:提交代码,我将上面写的代码放到leetcode,点击右下角的“Submit Solution”,就可以看到下面的“Submission Solution:Accepted”,就表示通过了,如下图:

      

       这样就OK了。

      三、测试程序

      这部分主要说明一下怎么去自己测试程序的运行?主要实现链表的创建、遍历、销毁(C++堆上内存要自己管理)。

      1、创建链表

      将数组传给函数,根据数组实现链表赋值;还会传入n创建多大的链表,代码如下:

    // 根据n个元素的数组arr创建一个链表, 并返回链表的头
    ListNode* createLinkedList(int arr[], int n){
    
        if(n == 0)
            return NULL;
    
        ListNode* head = new ListNode(arr[0]);
        ListNode* curNode = head;
        for(int i = 1 ; i < n ; i ++){
            curNode->next = new ListNode(arr[i]);
            curNode = curNode->next;
        }
    
        return head;
    }

      注意:创建的链表,没有真实的“头结点”,就是只存一个开始指针的节点,所以删除第一个节点要注意!

      2、遍历链表

      通过头结点进行遍历链表,代码如下:

    // 打印以head为头结点的链表信息内容
    void printLinkedList(ListNode* head){
    
        ListNode* curNode = head;
        while(curNode != NULL){
            cout << curNode->val << " -> ";
            curNode = curNode->next;
        }
    
        cout << "NULL" << endl;
    
        return;
    }

      3、销毁链表

      将创建时分配的内存释放,代码如下:

    // 释放以head为头结点的链表空间
    void deleteLinkedList(ListNode* head){
    
        ListNode* curNode = head;
        while(curNode != NULL){
            ListNode* delNode = curNode;
            curNode = curNode->next;
            delete delNode;
        }
    
        return;
    }

      4、测试程序

      对反转链表代码进行测试,整体代码如下:

    #include <iostream>
    
    using namespace std;
    
    /**
     * Definition for singly-linked list.
     */
    struct ListNode {
         int val;
         ListNode *next;
         ListNode(int x) : val(x), next(NULL) {}
    };
    
    /// LinkedList 测试辅助函数
    
    // 根据n个元素的数组arr创建一个链表, 并返回链表的头
    ListNode* createLinkedList(int arr[], int n){
    
        if(n == 0)
            return NULL;
    
        ListNode* head = new ListNode(arr[0]);
        ListNode* curNode = head;
        for(int i = 1 ; i < n ; i ++){
            curNode->next = new ListNode(arr[i]);
            curNode = curNode->next;
        }
    
        return head;
    }
    
    // 打印以head为头结点的链表信息内容
    void printLinkedList(ListNode* head){
    
        ListNode* curNode = head;
        while(curNode != NULL){
            cout << curNode->val << " -> ";
            curNode = curNode->next;
        }
    
        cout << "NULL" << endl;
    
        return;
    }
    
    // 释放以head为头结点的链表空间
    void deleteLinkedList(ListNode* head){
    
        ListNode* curNode = head;
        while(curNode != NULL){
            ListNode* delNode = curNode;
            curNode = curNode->next;
            delete delNode;
        }
    
        return;
    }
    
    
    // 206. Reverse Linked List
    // https://leetcode.com/problems/reverse-linked-list/description/
    // 时间复杂度: O(n)
    // 空间复杂度: O(1)
    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
    
            ListNode* pre = NULL;
            ListNode* cur = head;
            while(cur != NULL){
                ListNode* next = cur->next;
                cur->next = pre;
                pre = cur;
                cur = next;
            }
    
            return pre;
        }
    };
    
    int main(){
    
        int arr[] = {1, 2, 3, 4, 5};
        int n = sizeof(arr)/sizeof(int);
    
        ListNode* head = createLinkedList(arr, n);
        printLinkedList(head);
    
        ListNode* head2 = Solution().reverseList(head);
        printLinkedList(head2);
    
        deleteLinkedList(head2);
    
        return 0;
    }
    View Code

      运行结果如下:

      

      进行了反转,没有问题;

      四、删除链表元素

       1、题目

      LeetCode上第203题目:Remove Linked List Elements,题目如下:

    从具有值val的整数链表中删除所有元素。
    例子:
    输入:1->2->6->3->4->5->6,val = 6
    输出:1 - > 2 - > 3 - > 4 - > 5

      2、分析题目

      先来分析一下题目,用图来解释如下:

      

      假如删除值为4的节点,先把4的next指针保存,在3号位置指向5,这完全没有问题;但问题会发生在第一个节点位置,它没有前一个节点,那怎么办呢?在前面创建链表时也说过:没有头结点,所以使用虚拟头结点

      代码如下:

    // 203. Remove Linked List Elements
    // https://leetcode.com/problems/remove-linked-list-elements/description/
    // 使用虚拟头结点
    // 时间复杂度: O(n)
    // 空间复杂度: O(1)
    class Solution {
    public:
        ListNode* removeElements(ListNode* head, int val) {
    
            // 创建虚拟头结点
            ListNode* dummyHead = new ListNode(0);
            dummyHead->next = head;
    
            ListNode* cur = dummyHead;
            while(cur->next != NULL){
                if(cur->next->val == val){
                    ListNode* delNode = cur->next;
                    cur->next = delNode->next;
                    delete delNode;
                }
                else
                    cur = cur->next;
            }
    
            ListNode* retNode = dummyHead->next;
            delete dummyHead;
    
            return retNode;
        }
    };

      3、测试

      测试程序也是上面的链表创建和遍历,程序如下:

    #include <iostream>
    
    using namespace std;
    
    ///Definition for singly-linked list.
    struct ListNode {
        int val;
        ListNode *next;
        ListNode(int x) : val(x), next(NULL) {}
    };
    
    /// LinkedList Test Helper Functions
    ListNode* createLinkedList(int arr[], int n){
    
        if(n == 0)
            return NULL;
    
        ListNode* head = new ListNode(arr[0]);
        ListNode* curNode = head;
        for(int i = 1 ; i < n ; i ++){
            curNode->next = new ListNode(arr[i]);
            curNode = curNode->next;
        }
    
        return head;
    }
    
    void printLinkedList(ListNode* head){
    
        if(head == NULL){
            cout << "NULL" << endl;
            return;
        }
    
        ListNode* curNode = head;
        while(curNode != NULL){
            cout << curNode->val;
            if(curNode->next != NULL)
                cout << " -> ";
            curNode = curNode->next;
        }
    
        cout << endl;
    
        return;
    }
    
    void deleteLinkedList(ListNode* head){
    
        ListNode* curNode = head;
        while(curNode != NULL){
            ListNode* delNode = curNode;
            curNode = curNode->next;
            delete delNode;
        }
    
        return;
    }
    
    // 203. Remove Linked List Elements
    // https://leetcode.com/problems/remove-linked-list-elements/description/
    // 使用虚拟头结点
    // 时间复杂度: O(n)
    // 空间复杂度: O(1)
    class Solution {
    public:
        ListNode* removeElements(ListNode* head, int val) {
    
            // 创建虚拟头结点
            ListNode* dummyHead = new ListNode(0);
            dummyHead->next = head;
    
            ListNode* cur = dummyHead;
            while(cur->next != NULL){
                if(cur->next->val == val){
                    ListNode* delNode = cur->next;
                    cur->next = delNode->next;
                    delete delNode;
                }
                else
                    cur = cur->next;
            }
    
            ListNode* retNode = dummyHead->next;
            delete dummyHead;
    
            return retNode;
        }
    };
    
    int main() {
    
        int arr[] = {1, 2, 6, 3, 4, 5, 6};
        int n = sizeof(arr) / sizeof(int);
    
        ListNode* head = createLinkedList(arr, n);
        printLinkedList(head);
    
        Solution().removeElements(head, 6);
        printLinkedList(head);
    
        deleteLinkedList(head);
    
        return 0;
    }
    View Code

      运行结果如下:

      

       总结:

      希望通过这篇博客,大家能对基本的链表算法题能轻松应对;欢迎点赞,不懂的欢迎随时评论!多多支持,谢谢!

      

  • 相关阅读:
    SMTP发邮件(直接可用)实例
    ADO.NET(二)
    ADO.NET(一)
    C# 反射(一)
    APサーバ
    DB2 相关操作
    ArrayList与LinkedList时间复杂度之对比
    java泛型问题 关于警告:XX is a raw type
    Java编程中提高性能的几点建议
    STRUTS2核心控制器:FilterDispatcher
  • 原文地址:https://www.cnblogs.com/liudw-0215/p/9855221.html
Copyright © 2011-2022 走看看