C++单链表的初始化,插入,删除,反转操作

本文深入解析链表的基本操作,包括初始化、节点插入、节点删除以及链表的倒序输出。通过实例代码,详细阐述了每个操作的实现过程,帮助读者理解链表在数据结构中的应用。

链表的性质:

1.元素不能随机访问

2.链表只有一个表头

3.元素相互依赖,串联而成

链表的初始化:

#include<iostream>
using namespace std;
//节点类
class Node{
public:
    int data;       //数据域
    Node *next;     //指向下一个节点的指针
    Node(int _data){  //显式构造函数,每次对新声明的节点类的对象进行初始化
        data = _data;
        next = NULL;
    }
};
//链表类
class LinkList{
public:
    LinkList (){
        head = NULL;    //显式构造函数,每次对新声明的链表类进行初始化,即表示
                        //创建一个新的"空"链表,链表中只有头指针,并且头指针的值为NULL,表示头指针
                        //指向的部分也无任何数据元素
    }
    void Insert(Node *node,int position);
    void Output();
private:
    Node *head;    //设置为类的私有成员,只有通过类的成员函数才能访问
};


链表的插入操作以及链表节点的遍历:

                //C++形式的链表的插入,遍历输出,链表倒序等操作
#include<iostream>
using namespace std;
//节点类
class Node{
public:
    int data;       //数据域
    Node *next;     //指向下一个节点的指针
    Node(int _data){  //显式构造函数,每次对新声明的节点类的对象进行初始化
        data = _data;
        next = NULL;
    }
};
//链表类
class LinkList{
public:
    LinkList (){
        head = NULL;    //显式构造函数,每次对新声明的链表类进行初始化,即表示
                        //创建一个新的"空"链表,链表中只有头指针,并且头指针的值为NULL,表示头指针
                        //指向的部分也无任何数据元素
    }
    void Insert(Node *node,int position);
    void Output();
private:
    Node *head;    //设置为类的私有成员,只有通过类的成员函数才能访问
};
void LinkList::Insert(Node *node,int position){     //position表示要插入的位置
            if(head==NULL){              //在头指针为空指针的情况下,链表是空的,无任何数据元素
                                        //包括头结点在内.因此呢,我们在插入节点的时候,如果发现头结点为空指针
                head = node;             //的情况,那么这就意味着我们插入的这个节点要将其设置为头结点,
                return;                 //而后一一插入的节点则接在该节点的后方,并且通过这个节点从头开始遍历
                                        //能够遍历到链表中所有的数据元素


            }
            if(position==0){      //插入的是第0个位置,为了方便,我们暂且规定头结点为第0个节点
                                  //那么如果插入的是第0个位置的话,说明插入的节点要成为新的头结点
                                  //而不管之前有没有头结点
            node -> next = head;
            head = node;          //完成头结点的更新,表示头结点现在是node节点
            return;

            }
            Node *current_node = head;     //从头结点开始遍历插入的位置
            int i = 0;                    //表示已经遍历过的节点数目
            while(current_node -> next != NULL && i < position-1){
                                                                //不断寻找要插入的位置的前一个位置,然后插入在这个位置的节点与下一个位置的节点之间
                current_node = current_node -> next;            //不断更新current_node的值
                i++;
            }
            if(i == position-1){            //找到了插入的位置,先更新插入节点的指针域,记录当前遍历到的位置的下一个节点
                                            //然后再接上前面的节点

                node -> next = current_node -> next;
                current_node -> next = node;
            }
}
void LinkList::Output(){
            if(head==NULL){
                return;       //链表为空链表,无任何数据元素可输出
            }
            Node *current_node = head;     //从头结点开始遍历
            while(current_node!=NULL){
                cout << current_node -> data << " ";
                current_node = current_node -> next;
            }
            cout << endl;
}
int main(){
    LinkList linkList;
    for(int i = 1;i <= 10;i++){
        Node *node = new Node(i);      //用new给链表的每一个节点开辟空间
        linkList.Insert(node,i-1);     //调用成员函数
    }
    linkList.Output();
    return 0;
}


链表的节点删除操作

                //C++形式的链表的插入,遍历输出,链表倒序等操作
#include<iostream>
using namespace std;
//节点类
class Node{
public:
    int data;       //数据域
    Node *next;     //指向下一个节点的指针
    Node(int _data){  //显式构造函数,每次对新声明的节点类的对象进行初始化
        data = _data;
        next = NULL;
    }
};
//链表类
class LinkList{
public:
    LinkList (){
        head = NULL;    //显式构造函数,每次对新声明的链表类进行初始化,即表示
                        //创建一个新的"空"链表,链表中只有头指针,并且头指针的值为NULL,表示头指针
                        //指向的部分也无任何数据元素
    }
    void Insert(Node *node,int position);
    void Output();
    void delete_node(int delete_position);
private:
    Node *head;    //设置为类的私有成员,只有通过类的成员函数才能访问
};
void LinkList::Insert(Node *node,int position){     //position表示要插入的位置
            if(head==NULL){              //在头指针为空指针的情况下,链表是空的,无任何数据元素
                                        //包括头结点在内.因此呢,我们在插入节点的时候,如果发现头结点为空指针
                head = node;             //的情况,那么这就意味着我们插入的这个节点要将其设置为头结点,
                return;                 //而后一一插入的节点则接在该节点的后方,并且通过这个节点从头开始遍历
                                        //能够遍历到链表中所有的数据元素


            }
            if(position==0){      //插入的是第0个位置,为了方便,我们暂且规定头结点为第0个节点
                                  //那么如果插入的是第0个位置的话,说明插入的节点要成为新的头结点
                                  //而不管之前有没有头结点
            node -> next = head;
            head = node;          //完成头结点的更新,表示头结点现在是node节点
            return;

            }
            Node *current_node = head;     //从头结点开始遍历插入的位置
            int i = 0;                    //表示已经遍历过的节点数目
            while(current_node -> next != NULL && i < position-1){
                                                                //不断寻找要插入的位置的前一个位置,然后插入在这个位置的节点与下一个位置的节点之间
                current_node = current_node -> next;            //不断更新current_node的值
                i++;
            }
            if(i == position-1){            //找到了插入的位置,先更新插入节点的指针域,记录当前遍历到的位置的下一个节点
                                            //然后再接上前面的节点

                node -> next = current_node -> next;
                current_node -> next = node;
            }
}
void LinkList::Output(){
            if(head==NULL){
                return;       //链表为空链表,无任何数据元素可输出
            }
            Node *current_node = head;     //从头结点开始遍历
            while(current_node!=NULL){
                cout << current_node -> data << " ";
                current_node = current_node -> next;
            }
            cout << endl;
}
void LinkList::delete_node(int delete_position){
           if(head==NULL){
                return;       //链表为空链表,无任何数据元素可删除
            }
           Node *current_node = head;     //从头结点开始遍历删除的位置
           int i = 0;                    //表示已经遍历过的节点数目
           if(delete_position==0){
                 head = head->next;     //删除的是第0个节点,即删除的是头结点,那么新的头结点即为
                                       //第一个节点
                 delete current_node;   //删除原来头结点申请的内存,防止内存泄漏
                 return;
           }
           while(current_node -> next != NULL && i < delete_position-1){
                                                                //不断寻找要删除的位置的前一个位置,然后插入在这个位置的节点与下一个位置的节点之间
                current_node = current_node -> next;            //不断更新current_node的值
                i++;
            }
            if(i == delete_position-1){            //找到了删除的位置
                Node *delete_node = current_node -> next;     //delete_node用于记住需要删除的节点的地址
                current_node -> next = current_node -> next -> next;
                delete delete_node;      //同样是将申请的内存归还给系统,防止内存泄漏
            }

}
int main(){
    LinkList linkList;
    for(int i = 1;i <= 10;i++){
        Node *node = new Node(i);      //用new给链表的每一个节点开辟空间
        linkList.Insert(node,i-1);     //调用成员函数
    }
    linkList.Output();
    linkList.delete_node(9);     //总共10个节点,i==1时为头结点,后九个节点一一接在头结点之后
    linkList.Output();
    return 0;
}





链表的倒序输出(即原来链表中各数据域存储的依次是1,2,3,4,5,6,7,8,9,10)

而倒序是将原来的头结点变成尾节点,原来的尾节点变成头结点。

倒序后将输出(10,9,8,7,6,5,4,3,2,1)

首先,链表的倒序的概念上面已经描述清楚了,首先我们应该清楚实现链表反转我们需要做哪些步骤?

1.断线(除尾节点外的所有节点都要“断线”)

2.加线(除头结点外的所有节点都要”加线“)

3.处理的节点前移一个,然后再重复1,2步骤,直到处理的节点为NULL,表示链表全部处理完毕

一开始,我们要知道的是,原来的头结点变成了尾节点,并且一开始是处理头结点。所以呢,原来的头结点的指针域应该为NULL,这样的话头结点与第一个节点便断开了,

然后便是“加线”,(线箭头的指向与原来链表的相反)加线是从第一个节点开始处理,于是我们声明一个current_node指针指向当前处理到的节点。

由于"加线"的是要将当前处理到的节点的指针域指向“断线”的节点(例如在第一个节点的“加线”操作中就是current_node -> next = head,即是第一个节点指向原来的头结点)

但是如果这样的话,当前节点便与之后的节点全部断开(原因是原来当前节点记录的是其下一个节点的地址,也就是说第一个节点记录的是第二个节点的地址),使得链表反转操作无法继续进行,所以呢,我们自然而然的就想到了再声明一个指向Node类型的next_node指针,使其先记住当前操作到的节点之后的节点的地址,即next_node = current_node -> next,这样的话,等到下一次操作的时候,直接将next_node的地址赋给current_node就好了。(表示指针前移)

然后在这期间要不断地更新头结点,因为我们可以不管链表有多长,但是呢,我们每操作一次,比如说是操作到第一个节点,使其指向头结点,那么这时候新的“头结点”就是第一个节点了,后面的话以此类推,直到操作完毕。

完整代码实现:

                //C++形式的链表的插入,遍历输出,链表倒序等操作
#include<iostream>
#include<cstdlib>
using namespace std;
//节点类
class Node{
public:
    int data;       //数据域
    Node *next;     //指向下一个节点的指针
    Node(int _data){  //显式构造函数,每次对新声明的节点类的对象进行初始化
        data = _data;
        next = NULL;
    }
};
//链表类
class LinkList{
public:
    LinkList (){
        head = NULL;    //显式构造函数,每次对新声明的链表类进行初始化,即表示
                        //创建一个新的"空"链表,链表中只有头指针,并且头指针的值为NULL,表示头指针
                        //指向的部分也无任何数据元素
    }
    void Insert(Node *node,int position);
    void Output();
    void Reverse_order();
private:
    Node *head;    //设置为类的私有成员,只有通过类的成员函数才能访问
};
void LinkList::Insert(Node *node,int position){     //position表示要插入的位置
            if(head==NULL){              //在头指针为空指针的情况下,链表是空的,无任何数据元素
                                        //包括头结点在内.因此呢,我们在插入节点的时候,如果发现头结点为空指针
                head = node;             //的情况,那么这就意味着我们插入的这个节点要将其设置为头结点,
                return;                 //而后一一插入的节点则接在该节点的后方,并且通过这个节点从头开始遍历
                                        //能够遍历到链表中所有的数据元素


            }
            if(position==0){      //插入的是第0个位置,为了方便,我们暂且规定头结点为第0个节点
                                  //那么如果插入的是第0个位置的话,说明插入的节点要成为新的头结点
                                  //而不管之前有没有头结点
            node -> next = head;
            head = node;          //完成头结点的更新,表示头结点现在是node节点
            return;

            }
            Node *current_node = head;     //从头结点开始遍历插入的位置
            int i = 0;                    //表示已经遍历过的节点数目
            while(current_node -> next != NULL && i < position-1){
                                                                //不断寻找要插入的位置的前一个位置,然后插入在这个位置的节点与下一个位置的节点之间
                current_node = current_node -> next;            //不断更新current_node的值
                i++;
            }
            if(i == position-1){            //找到了插入的位置,先更新插入节点的指针域,记录当前遍历到的位置的下一个节点
                                            //然后再接上前面的节点

                node -> next = current_node -> next;
                current_node -> next = node;
            }
}
void LinkList::Output(){
            if(head==NULL){
                return;       //链表为空链表,无任何数据元素可输出
            }
            Node *current_node = head;     //从头结点开始遍历
            while(current_node!=NULL){
                cout << current_node -> data << " ";
                current_node = current_node -> next;
            }
            cout << endl;
}
void LinkList::Reverse_order(){
           if(head==NULL){
                return;       //链表为空链表
            }
            Node *current_node = head -> next,*next_node;     //从头结点开始遍历
            head -> next = NULL;
            while(current_node != NULL){
                next_node = current_node -> next;    //记住当前操作节点的后面节点的地址
                current_node -> next = head;
                head = current_node;
                current_node = next_node;
            }
}
int main(){
    LinkList linkList;
    for(int i = 1;i <= 10;i++){
        Node *node = new Node(i);      //用new给链表的每一个节点开辟空间
        linkList.Insert(node,i-1);     //调用成员函数
    }
    linkList.Output();
    linkList.Reverse_order();
    linkList.Output();
    return 0;
}


由于在数据结构课程中已经有学过链表的相关内容,因此其他各式各样的链表详细的基本操作便不再补充,只简要的说明一下其他链表的一些性质

1.双向链表,也叫双链表。单链表里的指针域只记录了结点的下一个结点,也就是后继结点,而双向链表的指针域同时还记录了结点的上一个结点,也就是前驱结点。有了这样的结构,我们可以从头结点遍历到尾结点,也可以从尾结点遍历到头结点了。


注意双链表与单链表的区别:

双向链表的指针域有两个指针,一个指向后继,一个指向前驱

而单链表的指针域只有一个指向后继节点的指针

2.循环链表。相比单链表,循环链表是将最后一个结点的指针指向头结点,这样使得链表更加灵活方便。循环链表里没有空指针,所以在判断结束条件时,不再是判断指针是否为空,而是判断指针是否等于某固定指针。另外,在单链表里只能访问到后面的结点,而在循环链表里可以访问到所有的结点。


注意循环链表的问题:

1.循环链表遍历时只能用结点是否等于某一结点判断结束

2.循环链表可以访问到任意结点



如有错误,还请指正,O(∩_∩)O谢谢


评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值