【算法篇】链表专题

  前言:

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

  1、简介

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

  2、反转链表

  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,以下:ide

  

  

    第二步:点击“Create Account”,建立本身的用户,须要填写邮箱,须要点击连接进行激活,不然刷题没法提交;刚开始写的邮箱没给我发邮件,又在我的资料里从新换了邮箱,就能够收到了;函数

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

 

  

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

  

   这样就OK了。

  3、测试程序

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

  一、建立链表

  将数组传给函数,根据数组实现链表赋值;还会传入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;
}

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

  二、遍历链表

  经过头结点进行遍历链表,代码以下:

// 打印以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;
}

  四、测试程序

  对反转链表代码进行测试,总体代码以下:

#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

  运行结果以下:

  

  进行了反转,没有问题;

  4、删除链表元素

   一、题目

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

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

  二、分析题目

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

  

  假如删除值为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;
    }
};

  三、测试

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

 

#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

  运行结果以下:

  

   总结:

  但愿经过这篇博客,你们能对基本的链表算法题能轻松应对;欢迎点赞,不懂的欢迎随时评论!多多支持,谢谢!

相关文章
相关标签/搜索