链表学习笔记

单向链表

struct ListNode {// 单向链表
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

从尾到头打印链表

vector<int> TravelListFromTailToHead(struct ListNode* head) {// 【从尾到头打印链表】
    vector<int> result;
    if(NULL == head)
        return result;
    ListNode *p = head;
    stack<ListNode*> nodeStack;
    while(p != NULL) {
        nodeStack.push(p);
        p = p->next;
    }
    while(!nodeStack.empty()) {
        p = nodeStack.top();
        result.push_back(p->val);
        nodeStack.pop();
    }
    return result;
}

链表中倒数第 k 个结点

ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {// 【链表中倒数第 k 个结点】
    if(NULL == pListHead || 0 == k) return NULL;
    ListNode *p1 = pListHead, *p2 = pListHead;// 使用快慢指针
    while(p2->next && k-- > 1) {// k-- > 1 一定要放在后面
        p2 = p2->next;
    }
    if(k > 1) return NULL;// 说明 k 值比节点数还大
    while(p2->next) {
        p2 = p2->next;
        p1 = p1->next;
    }
    return p1;
}

反转链表

ListNode* ReverseList(ListNode* pHead) {// 【反转链表】
    if(NULL == pHead) return NULL;
    ListNode *p = pHead, *pNext = pHead->next, *pPre = NULL;// 需要定义三个指针
    while(pNext) {
        p->next = pPre;
        pPre = p;
        p = pNext;
        pNext = pNext->next;
    }
    p->next = pPre;
    return p;
}

合并两个排序的链表

ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {// 【合并两个排序的链表】
    if(NULL == pHead1) return pHead2;
    if(NULL == pHead2) return pHead1;
    ListNode *pHead = NULL, *p;// pHead:保存新链表表头;p:用于新链表遍历生成
    if(pHead1->val <= pHead2->val) {
        pHead = pHead1;
        pHead1 = pHead1->next;
    } else {
        pHead = pHead2;
        pHead2 = pHead2->next;
    }
    p = pHead;
    while(pHead1 && pHead2) {
        if(pHead1->val <= pHead2->val) {
            p->next = pHead1;
            pHead1 = pHead1->next;
        } else {
            p->next = pHead2;
            pHead2 = pHead2->next;
        }
        p = p->next;
    }
    while(pHead1) {
        p->next = pHead1;
        p = p->next;
        pHead1 = pHead1->next;
    }
    while(pHead2) {
        p->next = pHead2;
        p = p->next;
        pHead2 = pHead2->next;
    }
    return pHead;
}

两个链表的第一个公共结点

ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {// 【两个链表的第一个公共结点】
    ListNode *p = pHead1, *q = pHead2;
    // 先计算两个链表的长度
    unsigned int nLength1 = 0, nLength2 = 0;
    while(p != NULL && q != NULL) {
        nLength1++;
        nLength2++;
        p = p->next;
        q = q->next;
    }
    while(p != NULL) {
        nLength1++;
        p = p->next;
    }
    while(q != NULL) {
        nLength2++;
        q = q->next;
    }
    // 根据两链表的长度差,长的链表先走这个差值的步数
    int n = abs(nLength1 - nLength2);
    p = pHead1;
    q = pHead2;
    if(nLength1 < nLength2) {
        p = pHead2;
        q = pHead1;
    }
    while(n--)
        p = p->next;
    // 对齐后,一起走,直到遇到第一个公共节点
    while(p != NULL && q != NULL && p != q) {
        p = p->next;
        q = q->next;
    }
    return p;
}

链表中环的入口结点

ListNode* getMeetNode(ListNode* pHead) {// 用快慢指针,必然相会于环内某个节点,返回这个节点,否则返回 NULL
    if(NULL == pHead) return NULL;
    ListNode* pSlow = pHead->next;
    if(NULL == pSlow) return NULL;
    ListNode* pFast = pSlow->next;
    while(pSlow != NULL && pFast != NULL) {
        if(pFast == pSlow)
            return pFast;
        pFast = pFast->next;
        pSlow = pSlow->next;
        if(pFast != pSlow)
            pFast = pFast->next;
    }
    return NULL;
}
ListNode* EntryNodeOfLoop(ListNode* pHead) {// 【链表中环的入口结点】
    ListNode* meetNode = getMeetNode(pHead);// 用快慢指针得到环内某个节点
    if(NULL == meetNode)
        return NULL;
    int n = 1;
    // 用这个环内节点计算得到环内节点数
    ListNode* p = meetNode->next;
    while(p != meetNode) {
        p = p->next;
        ++n;
    }
    // 用两个指针,一个指针先走环内节点个数的步数,另一个节点在链表头
    // 当两个节点相遇时,此时的节点即是环的入口节点
    p = pHead;
    while(n--)
        p = p->next;
    ListNode* p1 = pHead;
    while(p1 != p) {
        p = p->next;
        p1 = p1->next;
    }
    return p;
}

判断链表是否是回文结构

bool IsPalindrome(ListNode* pHead) {// 【判断链表是否是回文结构】
    // 使用快慢指针,找到链表的中间
    ListNode* pFast = pHead;
    ListNode* pSlow = pHead;
    while(pFast != NULL && pSlow != NULL) {
        pFast = pFast->next;
        pSlow = pSlow->next;
        if(pFast != NULL)
            pFast = pFast->next;
    }

    pSlow = ReverseList(pSlow);// 从中间节点开始将链表反转(链表被改变,即现场改变)

    // 从两头开始往中间遍历,判断是否是回文结构
    bool flag = true;
    ListNode *p1 = pHead, *p2 = pSlow;
    while(p2 != NULL) {
        if(p1->val != p2->val) {
            flag = false;
        }
        p1 = p1->next;
        p2 = p2->next;
    }

    ReverseList(pSlow);// 将链表恢复(恢复现场)

    return flag;
}

删除链表中的重复结点

删除链表中相邻的重复结点(保留一个)

void DeleteDuplication(ListNode* &pHead) {// 【删除链表中相邻的重复结点(保留一个)】
    if(NULL == pHead) return;
    ListNode *p1 = pHead, *p2 = p1->next;
    while(p2 != NULL) {
        if(p2->val == p1->val) {
            while(p2 != NULL && p2->val == p1->val) {
                ListNode* p = p2;
                p2 = p2->next;
                delete p;
                p = NULL;
            }
            p1->next = p2;
        }
        if(NULL != p2)
            p2 = p2->next;
        p1 = p1->next;
    }
}

删除链表中重复结点(保留一个)

void DeleteDuplication2(ListNode* &pHead) {// 【删除链表中重复结点(保留一个)】
    if(NULL == pHead) return;
    set<int> s;
    s.insert(pHead->val);
    ListNode* p = pHead, *pNode = pHead->next;
    while(pNode != NULL) {
        if(s.find(pNode->val) == s.end()) {
            s.insert(pNode->val);
            p->next = pNode;
            p = p->next;
            pNode = pNode->next;
        } else {
            ListNode* q = pNode;
            pNode = q->next;
            delete q;
            q = NULL;
            p->next = NULL;
        }
    }
}

删除链表中相邻重复结点

void DeleteDuplication3(ListNode* &pHead) {// 【删除链表中相邻重复结点】
    if(NULL == pHead) return;
    ListNode *pPre = NULL, *pNode = pHead;
    while(pNode != NULL) {
        ListNode* pNext = pNode->next;
        if(pNext != NULL && pNext->val == pNode->val) {
            const int val = pNode->val;
            ListNode* p = pNode;
            while(p != NULL && val == p->val) {
                pNext = p->next;
                delete p;
                p = pNext;
            }
            if(NULL == pPre)
                pHead = pNext;
            else
                pPre->next = pNext;
            pNode = pNext;
        } else {
            pPre = pNode;
            pNode = pNode->next;
        }
    }
}

复杂链表

struct RandomListNode {// 复杂链表
    int val;
    struct RandomListNode *next, *random;
    RandomListNode(int x) : val(x), next(NULL), random(NULL) {}
};

复杂链表的复制

RandomListNode* Clone(RandomListNode* pHead) {// 【复杂链表的复制】
    if(NULL == pHead) return NULL;
    RandomListNode *p = pHead;
    while(p != NULL) {
        RandomListNode *pNode = new RandomListNode(p->val);
        pNode->next = p->next;
        p->next = pNode;
        p = pNode->next;
    }

    p = pHead;
    RandomListNode *pClone;
    while(p != NULL) {
        pClone = p->next;
        if(p->random != NULL)
            pClone->random = p->random->next;
        p = pClone->next;
    }

    p = pHead;
    RandomListNode *pCloneHead = pHead->next;
//    pClone = pHead->next;
//    p = pClone->next;
//    while(p != NULL) {
//        pClone->next = p->next;
//        pClone = pClone->next;
//        p->next = pClone->next;
//        p = p->next;
//    }
    RandomListNode *tmp;
    while(p->next) {
        tmp = p->next;
        p->next = tmp->next;
        p = tmp;
    }

    return pCloneHead;
}
时间: 2024-09-28 18:35:33

链表学习笔记的相关文章

数据结构 链表学习笔记

#include<stdio.h> #include<stdlib.h> #include<string.h> struct stu_node{ char num[15]; int score; struct stu_node *next; }; struct stu_node *stu_create() { struct stu_node *head, *newN, *tail; char num[15]; int score; printf("请输入学生的

C++链表学习笔记

如果要保存一些数据类型相同的变量,比如n个int类型的变量,就可以存放在一个数组中,然后通过下标方便的访问.可是数组的缺点也比较多,第一个就是在声明数组的时候,数组的长度必须是明确的,即便是动态声明一个数组,处理器必须要知道长度才能在内存中找出一段连续的内存来保存你的变量.第二个缺点也就是上一句中说到的,数组在内存中的地址必须是连续的,这样就可以通过数组首地址再根据下标求出偏移量,快速访问数组中的内容.现在计算机内存越来越大,这也算不上什么缺点.第三个缺点,也是非常难以克服的缺点,就是在数组中插

C++学习笔记47:链表的概念与结点类模板

学堂在线学习笔记 链表的概念与结点类模板 顺序访问的线性群体--链表类 链表是一种动态数据结构,可以用来表示顺序访问的线性群体: 链表是由系列结点组成,结点可以在运行时动态生成: 每一个结点包括数据域和指向链表中下一个结点的指针(即下一个结点的地址).如链表中每个结点中只有一个指向后继结点的指针,则该链表称为单链表: 单链表的结点类模板 template <class T> void Node<T>::insertAfter(Node<T> *p) { //p结点指针域

《数据结构与算法分析》学习笔记(三)——链表ADT

今天简单学习了下链表,待后续,会附上一些简单经典的题目的解析作为学习的巩固 首先要了解链表,链表其实就是由一个个结点构成的,然后每一个结点含有一个数据域和一个指针域,数据域用来存放数据,而指针域则用来存放下一个结点的地址. 1.先给出结点的定义. typedef struct Node *PtrToNode; typedef PtrToNode List; typedef PtrToNode Position; struct Node { ElementType Element; Positio

Redis学习笔记

Redis学习笔记:Redis是什么?redis是开源BSD许可高级的key-vlue存储系统可以用来存储字符串哈希结构链表.结构.集合,因此常用来提供数据结构服务. redis和memcache相比的独特之处:1.redis可以用来做存储,而memcache是用来做缓存 这个特点主要因为其有"持久化"的功能.2.存储的数据有"结构",对于memcache来说,存储的数据只有1种类型"字符串"而 redis则可以存储字符串.链表.哈希机构.集合.

[原创] linux课堂-学习笔记-目录及概况

本学习笔记基于:网易云课堂-linux课堂 课时1Centos 6.4安装讲解46:14 课时2Centos 6.4桌面环境介绍与网络连接04:30 课时3 Linux目录结构介绍及内核与shell分析37:19 课时4 Linux获得帮助_网络配置_合理关机64:23 课时5 Linux文件权限详解45:47 课时6Linux文件权限详解45:47 课时7目录显示个性操作与全局环境变量的使用与注意···43:25 课时8复制与远程复制_文件查找实例_文件内容查看的··57:49 课时9Linu

《Linux内核分析》第六周学习笔记

<Linux内核分析>第六周学习笔记 进程的描述和创建 郭垚 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 [学习视频时间:1小时 撰写博客时间:2小时] [学习内容:进程创建的过程.使用gdb跟踪分析内核处理函数sys_clone] 一.进程的描述 1.1 进程描述符task_struct数据结构(一) 1. 进程控制块PCB——task_struct 为了管理进程,内核

Python学习笔记--未经排版

Python 学习笔记 Python中如何做到Print() 不换行 答:Print("输出内容",end='不换行的分隔内容'),其中end=后面为2个单引号 注:在Python 2.x中,Print "输出内容", 即在输出内容后加一逗号 Python中 is 和 == 的区别 答:Python中的对象包含三要素:id.type.value 其中id用来唯一标识一个对象,type标识对象的类型,value是对象的值 is判断的是a对象是否就是b对象,是通过id来

小猪的数据结构学习笔记(四)

小猪的数据结构学习笔记(四) 线性表之静态链表 --转载请注明出处:coder-pig 本章引言: 在二,三中中我们分别学习了顺序表中的线性表与单链表,线性表有点类似于 我们前面所学的数组,而单链表使用的最多的是指针,这里问个简单的问题, 如果是在以前没有指针的话,前辈先人们怎么实现单链表呢?大家思考下! 没有指针,那么用什么来代替呢?前辈先人们非常机智,想出了使用下标+游标的方式 来实现单链表的效果!也就是今天要讲的--静态链表! 当然你也可以直接跳过本章,因为有了单链表就没有必要用静态链表了