您的位置:首页 > 编程语言 > C语言/C++

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

2016-02-20 17:18 801 查看
链表的性质:

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谢谢
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: