「数据结构与算法」链表 —— 看这一篇就够了(超详细)

1,525 阅读5分钟

一、链表简介

        链表是一种物理存储单元上非连续、非顺序的存储结构,数据结构的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比数组快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而数组只需要O(1)

        使用链表结构可以克服数组需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。

二、链表创建 

         链表的的每一个结点都有存储数据元素的数据域与存储下一个结点地址的指针域,所以我们在创建新链表时需要设置一个结点结构体(如下代码块SingleLinkedListNode).且还要拥有一个head指针与tail指针

SLL为单向链表,DLL为双向链表,()为value,-->为指针(单向链表),````与____为指针(双向链表)
======================================================================
SLL[0](1)----->SLL[1](2)----->SLL[2](3)----->SLL[3](4)
  Head                                         Tail
这里我们创建了一个拥有四个元素的SLL
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DLL[0](1)`_`_`_`_`DLL[1](2)`_`_`_`_`DLL[2](3)`_`_`_`_`DLL[3](4)
  Head                                                  Tail
这里我们创建了一个拥有四个元素的DLL

        再附上两张图:

        单向链表: 

struct SingleLinkedListNode{
    int value;
    struct SingleLinkedListNode *next;
}*Shead=NULL,*Stail=NULL;

        如果是双向链表的话则需要有prev指针:

struct DoubleLinkedListNode{
    int value;
    struct DoubleLinkedListNode *next;
    struct DoubleLinkedListNode *prev;
}*Dhead=NULL,*Dtail=NULL;

        有了结点结构体,我们就可以进行单双链表创建了:

void CreateNewSingleLinkedList(int len){
    for (int i=0;i<len;i++){
        int inputvalue;
        cin >> inputvalue;
        SingleLinkedListNode *node = new SingleLinkedListNode;
        node->value = inputvalue;
        node->next = NULL;
        if (!Shead){Shead=node;Stail=node;}
        else{Stail->next=node;Stail=node;}
    }
}

void CreateNewDoubleLinkedList(int len){
    for (int i=0;i<len;i++){
        int inputvalue;
        cin >> inputvalue;
        DoubleLinkedListNode *node = new DoubleLinkedListNode;
        node->value = inputvalue;
        node->next = NULL;
        if (!Dhead){Dhead=node;Dtail=node;}
        else{Dtail->next=node;node->prev=Dtail;Dtail=node;}
    }
}

        先看单链表,我们将CreateNewSingleLinkedList的参数len作为单向链表的长度.接着用for循环依次输入每项数据元素的值,再创建一个新的节点(关键字new),并把它的value设成刚刚输入的值,再将它的next项设为NULL.接下来判读如果!Shead(即单链表中没有元素),将Shead设为新节点,如果已经有其他元素,再将现在的Stail的next项设为新节点,最后让Stail变成新节点.

        这样讲可能有点复杂,所以现在给大家一张图:

        双链表也一样,只不过在上述代码第21行(倒数第3行)多了一个新节点的prev项设为Dtail的操作 

二、链表输出 

         链表的输出从头指针head开始,依次打印数据元素的value,接着再将指针指向下一个元素(单双链表都适用):

void OutputSingleLinkedList(){
    SingleLinkedListNode *point = Shead;
    while (point) {
        cout << point->value << " ";
        point = point->next;
    }
    cout << endl;
}
void OutputDoubleLinkedList(){
    DoubleLinkedListNode *point = Dhead;
    while (point) {
        cout << point->value << " ";
        point = point->next;
    }
    cout << endl;
}

        接下来我们结合以上链表创建进行实践:

df2dcc0ce9c9436ebd64854499c53da3-00002.jpg?auth_key=4814836629-0-0-63ea3102c25905efd3bce9bf52c75ef2

单双链表创建及实践

 可以看到,我们的链表创建以及打印已经没有问题了

三、链表长度

        链表的长度也可以用遍历解决:

int SingleLinkedListLength(){
    SingleLinkedListNode *point = Shead;
    int len;
    while (point) {
        len++;
    }
    return len;
}

int DoubleLinkedListLength(){
    DoubleLinkedListNode *point = Dhead;
    int len;
    while (point) {
        len++;
    }
    return len;
}

四、链表元素改值 

        我们只需要把指针定位到元素上,再将它的value改变即可:

void SingleLinkedListChange(int pos,int newvalue){
    SingleLinkedListNode *point = Shead;
    for (int i=0;i<=pos;i++) {
        point = point->next;
    }
    point->value = newvalue;
}

void DoubleLinkedListChange(int pos,int newvalue){
    DoubleLinkedListNode *point = Dhead;
    for (int i=0;i<=pos;i++) {
        point = point->next;
    }
    point->value = newvalue;
}

五、链表插入 

         如果用嘴巴光说怎么操作其实是不太好的,所以我就给大家一张图:

​        

        从图中可以看出,我们想要插入15这个节点在10之后,于是我们将10的next项设为15,然后将15的next项设为20.注意,前面这段话如果用代码写出来是不对的(假设新插入节点的指针叫N,10的指针叫K):

K->next=N;N->next=K->next

        为什么不对呢,因为你如果先将10这个节点的下一个节点设为15,那15的下一个节点就又是15了——因为我们已经将10的下一个节点设为15,在将15的下一个节点设为10的下一个节点就没有任何意义了.所以上述代码应该反过来:

N->next=K->next;K->next=N

        而双向链表也是同样的,我们先设新插入节点的next与prev值,再设已有节点的prev与next值 (假设新插入节点的指针叫N,10的指针叫K) :

N->next = K->next;
N->prev = K;
k->next->prev = N;
k->next = N;

        好,把完整代码拿上来:

void SingleLinkedListInsert(int pos,int newvalue){
    SingleLinkedListNode *point = Shead;
    for (int i=0;i<pos-1;i++) {
        point = point->next;
    }
    SingleLinkedListNode *node = new SingleLinkedListNode;
    node->value=newvalue;
    node->next = point->next;
    point->next = node;
}

void DoubleLinkedListInsert(int pos,int newvalue){
    DoubleLinkedListNode *point = Dhead;
    for (int i=0;i<pos-1;i++) {
        point = point->next;
    }
    DoubleLinkedListNode *node = new DoubleLinkedListNode;
    node->value=newvalue;
    node->next = point->next;
    node->prev = point;
    if (!point->next){
        Dtail = node;
    }
    else{
        point->next->prev = node;
    }
    if (!point->prev){
        Dhead = node;
    }
    else{
        point->next = node;
    }
}

         注意在双向链表进行对表中已有元素的prev与next操作时,要判断 prev与next是否为NULL

六、链表删除

        删除的原理与插入差不多,大家可以自己想想,这里不再阐述:

void SingleLinkedListDelete(int pos){
    SingleLinkedListNode *point = Shead;
    for (int i=0;i<pos-1;i++) {
        point = point->next;
    }
    point->next = point->next->next;
}

void DoubleLinkedListDelete(int pos){
    DoubleLinkedListNode *point = Dhead;
    for (int i=0;i<pos;i++) {
        point = point->next;
    }
    DoubleLinkedListNode *s1 = point->prev,*s2 = point->next;
    if (s1!=NULL){
        s1->next = s2;
    }
    else{
        if (!pos){
            Dhead = s2;
        }
        else{
            Dhead = s1;
        }
    }
    if (s2!=NULL){
        s2->prev = s1;
    }
    else{
        Dtail = s1;
    }
}

七、链表元素查找 

         非常的简单,只要判断当前元素的值是否对应即可:

bool SingleLinkedListFind(int pos,int findvalue){
    SingleLinkedListNode *point = Shead;
    for (int i=0;i<=pos;i++) {
        if (point->value==findvalue) return true;
        point = point->next;
    }
    return false;
}

bool DoubleLinkedListFind(int pos,int findvalue){
    DoubleLinkedListNode *point = Dhead;
    for (int i=0;i<=pos;i++) {
        if (point->value==findvalue) return true;
        point = point->next;
    }
    return false;
}

八、结束语 

        好了,今天就到这里啦,如果这篇7500字的文章对你有帮助的话,请转发给更多的朋友一起学习,下期见(附完整代码)

#include <iostream>
using namespace std;

struct SingleLinkedListNode{
    int value;
    struct SingleLinkedListNode *next;
}*Shead=NULL,*Stail=NULL;

struct DoubleLinkedListNode{
    int value;
    struct DoubleLinkedListNode *next;
    struct DoubleLinkedListNode *prev;
}*Dhead=NULL,*Dtail=NULL;

void CreateNewSingleLinkedList(int len){
    for (int i=0;i<len;i++){
        int inputvalue;
        cin >> inputvalue;
        SingleLinkedListNode *node = new SingleLinkedListNode;
        node->value = inputvalue;
        node->next = NULL;
        if (!Shead){Shead=node;Stail=node;}
        else{Stail->next=node;Stail=node;}
    }
}

void CreateNewDoubleLinkedList(int len){
    for (int i=0;i<len;i++){
        int inputvalue;
        cin >> inputvalue;
        DoubleLinkedListNode *node = new DoubleLinkedListNode;
        node->value = inputvalue;
        node->next = NULL;
        if (!Dhead){Dhead=node;Dtail=node;}
        else{Dtail->next=node;node->prev=Dtail;Dtail=node;}
    }
}

void OutputSingleLinkedList(){
    SingleLinkedListNode *point = Shead;
    while (point) {
        cout << point->value << " ";
        point = point->next;
    }
    cout << endl;
}
void OutputDoubleLinkedList(){
    DoubleLinkedListNode *point = Dhead;
    while (point) {
        cout << point->value << " ";
        point = point->next;
    }
    cout << endl;
}

int SingleLinkedListLength(){
    SingleLinkedListNode *point = Shead;
    int len;
    while (point) {
        len++;
    }
    return len;
}

int DoubleLinkedListLength(){
    DoubleLinkedListNode *point = Dhead;
    int len;
    while (point) {
        len++;
    }
    return len;
}

void SingleLinkedListChange(int pos,int newvalue){
    SingleLinkedListNode *point = Shead;
    for (int i=0;i<=pos;i++) {
        point = point->next;
    }
    point->value = newvalue;
}

void DoubleLinkedListChange(int pos,int newvalue){
    DoubleLinkedListNode *point = Dhead;
    for (int i=0;i<=pos;i++) {
        point = point->next;
    }
    point->value = newvalue;
}

void SingleLinkedListInsert(int pos,int newvalue){
    SingleLinkedListNode *point = Shead;
    for (int i=0;i<pos-1;i++) {
        point = point->next;
    }
    SingleLinkedListNode *node = new SingleLinkedListNode;
    node->value=newvalue;
    node->next = point->next;
    point->next = node;
}

void DoubleLinkedListInsert(int pos,int newvalue){
    DoubleLinkedListNode *point = Dhead;
    for (int i=0;i<pos-1;i++) {
        point = point->next;
    }
    DoubleLinkedListNode *node = new DoubleLinkedListNode;
    node->value=newvalue;
    node->next = point->next;
    node->prev = point;
    if (!point->next){
        Dtail = node;
    }
    else{
        point->next->prev = node;
    }
    if (!point->prev){
        Dhead = node;
    }
    else{
        point->next = node;
    }
}

void SingleLinkedListDelete(int pos){
    SingleLinkedListNode *point = Shead;
    for (int i=0;i<pos-1;i++) {
        point = point->next;
    }
    point->next = point->next->next;
}

void DoubleLinkedListDelete(int pos){
    DoubleLinkedListNode *point = Dhead;
    for (int i=0;i<pos;i++) {
        point = point->next;
    }
    DoubleLinkedListNode *s1 = point->prev,*s2 = point->next;
    if (s1!=NULL){
        s1->next = s2;
    }
    else{
        if (!pos){
            Dhead = s2;
        }
        else{
            Dhead = s1;
        }
    }
    if (s2!=NULL){
        s2->prev = s1;
    }
    else{
        Dtail = s1;
    }
}

bool SingleLinkedListFind(int pos,int findvalue){
    SingleLinkedListNode *point = Shead;
    for (int i=0;i<=pos;i++) {
        if (point->value==findvalue) return true;
        point = point->next;
    }
    return false;
}

bool DoubleLinkedListFind(int pos,int findvalue){
    DoubleLinkedListNode *point = Dhead;
    for (int i=0;i<=pos;i++) {
        if (point->value==findvalue) return true;
        point = point->next;
    }
    return false;
}