1、前插法创建单链表
#include<stdio.h>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//头插法创建单链表
void CreateLinkList(LinkList &L,int n){
L = new LNode; //创建一个带头节点的单链表
L->next = NULL; //首元节点为空
cout<<"\n请输入要存储的整数值:";
for(int i = 0;i < n;i++){
LNode *p;
p = new LNode; //生成一个新节点*p
cin>>p->data;
p->next = L->next; //使新节点*p插入到头节点之后
L->next = p; //使头节点指针L指向当前插入的新节点
}
}
//打印创建的单链表
void PrintLinkList(LinkList L){
LNode *p;
p = L->next;
cout<<"\n头插法创建的单链表如下:";
while(p){
cout<<p->data<<" ";
p = p->next;
}
}
main(){
LinkList L;
int n;
cout<<"\n请输入使用头插法创建单链表的表长:";
cin>>n;
CreateLinkList(L,n);
PrintLinkList(L);
}
可以很清晰地看见,所谓头插法就是每一次将输入的元素都插入到头节点之后,这样做很明显的一个特点就是可以将我们按照正常顺序输入进去的元素可以最后逆序打印出来。所以可以使用头插法实现链表的原地逆置
设计一个算法,通过遍历一趟,将链表中所有结点的链接方向逆转,仍利用原表的
#include<stdio.h>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//头插法创建单链表
void CreateLinkList(LinkList &L,int n){
L = new LNode; //创建一个带头节点的单链表
L->next = NULL; //首元节点为空
cout<<"\n请输入要存储的整数值:";
for(int i = 0;i < n;i++){
LNode *p;
p = new LNode; //生成一个新节点*p
cin>>p->data;
p->next = L->next; //使新节点*p插入到头节点之后
L->next = p; //使头节点指针L指向当前插入的新节点
}
}
//打印创建的单链表
void PrintLinkList(LinkList L){
LNode *p;
p = L->next;
cout<<"\n头插法创建的单链表如下:";
while(p){
cout<<p->data<<" ";
p = p->next;
}
cout<<"\n";
}
//原地逆置单链表
void Reverse(LinkList &L)
{
LNode *p;
p=L->next; //找到首元节点,以首元节点开始遍历,此时相当于在一条没有头节点的链条上遍历,只是通过L找到这个链条的地址,再通过L找到首元节点地址
L->next=NULL; //p = L->next已经找到首元地址,所以可以将链条L从首元节点断开,直接遍历以p为开头的链条
LNode *q;
while ( p) {
q=p->next; //首先拿到*p之后的节点*q,知道下一个节点的地址
p->next=L->next; //使新节点*p插入到头节点之后
L->next=p; //使头节点指针L指向当前插入的新节点
p = q; //重新修改指针指向,循环遍历
}
}
//逆置之后打印单链表
void PrintReverseLinkList(LinkList L){
LNode *p;
p = L->next;
cout<<"\n逆置之后打印单链表如下:";
while(p){
cout<<p->data<<" ";
p = p->next;
}
}
main(){
LinkList L;
int n;
cout<<"\n请输入使用头插法创建单链表的表长:";
cin>>n;
CreateLinkList(L,n);
PrintLinkList(L);
Reverse(L);
PrintReverseLinkList(L); //逆置之后打印单链表
}
核心算法实现逆置单链表
//原地逆置单链表
void Reverse(LinkList &L)
{
LNode *p;
p=L->next; //找到首元节点,以首元节点开始遍历,此时相当于在一条没有头节点的链条上遍历,只是通过L找到这个链条的地址,再通过L找到首元节点地址
L->next=NULL; //p = L->next已经找到首元地址,所以可以将链条L从首元节点断开,直接遍历以p为开头的链条
LNode *q;
while ( p) {
q=p->next; //首先拿到*p之后的节点*q,知道下一个节点的地址
p->next=L->next; //使新节点*p插入到头节点之后
L->next=p; //使头节点指针L指向当前插入的新节点
p = q; //重新修改指针指向,循环遍历
}
}
原地逆置单链表的核心是拿到当前链表L的后继节点----首元节点的地址,然后将链表的头结点与首元节点断开,使用 L->next=NULL,这样链表就变成了一个空链表,断开后以p为首元节点的链表相当于一个没有头结点的链表,但是我们刚开始时使用p保存了断开后没有头结点的链条的起始地址,所以我们就可以直接顺序一边遍历出链表的每一个节点然后再次使用头插法即可完成单链表的逆置操作!
当然链表的逆置还有一种方式是首先定义一个新的链表L1,将老链表L中的每一个元素循环遍历,然后取下当前值再次使用头插法插入到L1Z中!
2、后插法创建单链表
#include<stdio.h>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//尾插法创建单链表
void CreateLinkList(LinkList &L,int n){
LNode *r;
L = new LNode; //创建一个带头节点的单链表
L->next = NULL; //首元节点为空
r = L; //尾指针r指向头节点
cout<<"\n请输入要存储的整数值:";
for(int i = 0;i < n;i++){
LNode *p;
p = new LNode; //生成一个新节点*p
cin>>p->data; //将输入元素值赋值给新节点*p的数据域
p->next = NULL;
r->next = p; //将新节点*p插入到尾节点*之后
r = p; //使r重新指向新的尾结点*p
}
}
//打印创建的单链表
void PrintLinkList(LinkList L){
LNode *p;
p = L->next;
cout<<"\n尾插法创建的单链表如下:";
while(p){
cout<<p->data<<" ";
p = p->next;
}
}
main(){
LinkList L;
int n;
cout<<"\n请输入使用尾插法创建单链表的表长:";
cin>>n;
CreateLinkList(L,n);
PrintLinkList(L);
}
3、使用递归实现单链表的输出
//使用递归实现单链表的输出操作
void ListPrint_L_Rec(LinkList L){
void ListPrint_L_Rec(LinkList L){
if(L==NULL){
return; //递归终止
}
else{
cout<<L->data<<" ";
ListPrint_L_Rec(L->next); //L指向后继节点继续递归
}
}
}
4、双向链表的插入和删除操作
插入操作
第一步:首先找到插入位置,节点 s 将插入到节点 p 之前
第二步:将节点 s 的前驱指向节点 p 的前驱,即 s->prior = p->prior;
第三步:将节点 p 的前驱的后继指向节点 s 即 p->prior->next = s;
第四步:将节点 s 的后继指向节点 p 即 s->next = p;
第五步:将节点 p 的前驱指向节点 s 即 p->prior = s;
可以理解为执行的操作是从1出发顺时针到4的操作!
s->prior = p->prior;
p->prior->next = s;
s->next = p;
p->prior = s;
删除操作
第一步:找到即将被删除的节点 p
第二步:将 p 的前驱的后继指向 p 的后继,即 p->prior->next = p->next;
第三步:将 p 的后继的前驱指向 p 的前驱,即 p->next->prior = p->prior;
第四步:删除节点 p 即 delete p;
p->prior->next = p->next;
p->next->prior = p->prior;
5、双向循环链表的插入和删除操作
在双向循环链表中插入操作与双向链表是一样的,只是由于双向循环链表对于插入或者删除不用担心有后继节点丢失,所以可以大胆地操作,不用管操作顺序是哪个