1.单链表基本知识点
2.单链表代码实现
1.带头结点(基本操作代码实现)
1.头插法可以实现单链表
逆置
2.偷天换日
(见文末代码)-----在某个结点前插入结点
3.头插法和尾插法中心思想-------结点后插法
(见文末代码)
4.注意代码封装
思想(见文末代码)
#include<bits/stdc++.h>
using namespace std;
//定义结点
typedef struct LNode{
int data; //存储数据
struct LNode *next; // 存储下一个结点的指针
}LNode,*LinkList; //通常LinkList代表单链表,LNode *代表某个结点
//0.初始化单链表
void InitList(LinkList &L){
L=(LNode *)malloc(sizeof(LNode));
L->next=NULL;
}
//创建单链表核心思想——结点后插法
//1.尾插法
LinkList TailCreate(int a[],int n){ //
LinkList L=(LNode *)malloc(sizeof(LNode)); // 创建单链表头节点
L->next=NULL; //末尾指针指向null
LNode *s,*r=L; //r始终指向最后一个结点 //声明的是指针变量
//核心思想——结点后插法 (在r后插入结点s)
for(int i=0;i<n;i++){
s=(LNode *)malloc(sizeof(LNode)); //为指针变量分配存储空间
s->data=a[i];
s->next=r->next;
r->next=s;
r=s;
}
return L;
}
//2.头插法——可以实现单链表逆置
LinkList HeadCreate(int a[],int n){
LinkList L=(LNode *)malloc(sizeof(LNode));
L->next=NULL;
LNode *s; //声明的是指针变量
//核心思想——结点后插法
for(int i=0;i<n;i++){
s=(LNode *)malloc(sizeof(LNode)); //为指针变量分配存储空间
s->data=a[i];
s->next=L->next;
L->next=s;
}
return L;
}
//1.插入
bool Insert(LinkList &L,int i,int e){
if(i<1){
return false;
}
LNode *p; //指针p指到当前扫描到的节点
int j=0; //指针p当前扫描到的位置
p=L; //p指向头节点,头节点是第0个节点
while(p!=NULL&&j<i-1){ // 找到第i-1个位置
p=p->next;
j++;
}
if(p==NULL){ // 检查i的值,即找到表末尾也未找到i-1的位置
return 0;
}
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=e;
//以下两个顺序不能颠倒
s->next=p->next;
p->next=s;
return true;
}
//2.删除
bool Delete(LinkList &L,int i,int &e){ //&e带回删除的值
if(i<1){
return false;
}
LNode *p;
int j=0;
p=L;
while(p!=NULL&&j<i-1){
p=p->next;
j++;
}
if(p==NULL){ //判断i的值是否合法
return false;
}
LNode *s=(LNode *)malloc(sizeof(LNode)); //方便释放结点空间
s=p->next;
e=s->data; //存储要删除结点的值
p->next=s->next;
free(s);
return true;
}
//3.按位查找
LNode * Get(LinkList L,int i){
if(i<1){
return NULL;
}
LNode *p=L; //指向正在扫描的结点
int j=0; //正在扫描的结点为第几个结点
while(p!=NULL&&j<i){
p=p->next;
j++;
}
return p;
}
//4.按值查找
int Locate(LinkList L,int e){
LNode *p=L; // 指向第一个存有数据的节点
int j=0;
while(p->next!=NULL){
p=p->next;
j++;
if(p->data==e){
return j; //查找成功,返回位置
}
}
return 0;
}
//5.单链表长度
int Length(LinkList L){
LNode *p=L;
int cnt=0;
while(p->next!=NULL){
p=p->next;
cnt++;
}
return cnt;
}
//6.循环遍历输出
void PrintList(LinkList L){
LNode *p=L;
while(p->next!=NULL){
p=p->next;
cout<<p->data<<" ";
}
cout<<endl;
}
//主程序
int main(){
LinkList L;
int a[]={1,2,3,4,5,6};
//0.测试头插法 和尾插法 遍历输出
// L=HeadCreate(a,6);
// PrintList(L);
L=TailCreate(a,6);
PrintList(L);
//1.测试插入
// if(Insert(L,3,8)){
// PrintList(L);
// } else{
// cout<<"插入异常!!!"<<endl;
// }
//2.测试删除
// int e=-1;
// if(Delete(L,3,e)){
// PrintList(L);
// cout<<e<<endl;
// } else{
// cout<<"删除异常!!!"<<endl;
// }
//3.测试按位查找
// LNode *p=Get(L,3);
// cout<<p->data<<endl;
//4.测试按值查找
// int i=Locate(L,3);
// cout<<i<<endl;
//5.测试单链表长度
// int len=Length(L);
// cout<<len<<endl;
return 0;
}
2.结点后插法 偷天换日 封装思想
//指定结点的后插操作
bool InsertNextNode(LNode *p,int e){
if(p==NULL){
return false;
}
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
//指定结点的前插入操作——偷天换日
bool InsertPriorNode(LNode *p,int e){
if(p==NULL){
return false;
}
LNode *s=(LNode *)malloc(sizeof(LNode));
//换值,并交换p、s结点的位置
s->data=p->data;
s->next=p->next;
p->next=s;
p->data=e;
return true;
}
// 删除指定结点——偷天换日
bool DeleteNode(LNode *p){
if(p==NULL){
return false;
}
//含有小bug,不能删除指定的表末最后一个结点
LNode *s=p->next;
p->data=s->data;
p->next=s->next;
free(s);
return true;
}
//封装思想测试
int main(){
LinkList L;
int a[]={1,2,3,4,5,6};
//0.尾插法 遍历输出
L=TailCreate(a,6);
PrintList(L);
//6.在封装的基础上,测试插入
//6.1查找i-1的位置
LNode *p=Get(L,2); //按位查找结点
//6.2在第i-1个节点后插入元素
if(InsertNextNode(p,8)){ //偷天换日
PrintList(L);
} else{
cout<<"插入异常!!!"<<endl;
}
return 0;
}