1.单链表的定义和表示:
任意一组存储单元存储线性表的数据元素(这组存储单元可连续也可不连续)结点包含两个域:数据域(存储元素信息)和指针域(存储直接后继位置(指针或链))。
n个结点连结成一个连结成一个链表即为线性表
特点:不要求逻辑上相邻的两个元素物理上也相邻
通过“链”建立起数据元素之间的逻辑关系
插入、删除不需要移动数据元素,只需要改链
但对于访问序号为i的元素和求线性表的长度为多少就比顺序表复杂
typedef struct LNode*List;
struct LNode{
ElementType Data;
List Next;
};
struct LNode L;
List Ptrl;
1.求表长
int Length (List Ptrl){
List p=Ptrl;//p指向表的第一个结点
int j=0;
while(p){
p=p->Next;//当前p指向的时第j个结点
j++;
}
return j;
}
查找
//查找
//(1)按序号查找:FindKth
List FindKth(int K,List Ptrl){
List p=Ptrl;
int i=1;
while (p!=NULL&&i<K){
p=p->Next;
i++;
}
if(i==K)return p
else return NULL;
}
//(2)按值查找Find
List Find(ElementType X,List Ptrl){
List p=Ptrl;
while(p!=NULL&&p->Data!=X)
p=p->Next;
return p;
}
//插入
List Insert(ElementType X,int i,List Ptrl){
List p,s;
if(i==1){//新结点插在表头
s=(List)malloc(sizeof(struct LNode));
s->Data=X;
s->Next=Ptrl;
return s;
}
//如果不是第一个则需要判断输入的i是否合法,由于还需考虑,表的长度,表是否为空之类的问题
//直接判断要插入的位置的前面是否还有元素,如果没有,则说明这个位置错误
//若果有,则插入
p=FindKth(i-1,Ptrl);
if(p==NULL){
printf("参数错误");
return NULL;
}
else{
s=(List)malloc(sizeof(struct LNode));
s->Data=X;
s->Next=p->Next;//只需要考虑要插入的位置前面的结点的关系,替代它的后继
p->Next=s;//改变它的后继
return Ptrl;
}
}
删除
先找到链表的第i-1个结点,用p指向
再用指针s指向要被删除的结点(p的下一个结点);
然后修改指针,删除s所指结点
最后释放s结点的空间
List Delete(int i,List Ptrl) {
List p,s;
if(i==1){
s=Ptrl;
if(Ptrl!=NULL)
Ptrl=Ptrl->Next;
else return NULL;//如果这个头指针是空的,后面没有结点了,删了不就都空了吗?
free(s);
return Ptrl;
}
}
p=FindKth(i-1,Ptrl);
if(p==NULL){
printf("第%d个结点不存在",i-1);return NULL;
}
else if(p->Next==NULL){
printf("第%d个结点不存在",i);return NULL;
}
else {
s=p->Next;
p->Next=s->Next;
free(s);
return Ptrl;
}
另一种方法:数据结构书上的
先认识几个概念
首元结点:链表存储中的第一个元素
头结点:首元结点之前的特意设置的一个结点,其指针域指向首元结点,数据域可为空
头指针:第一个结点的指针,若有头结点则为头结点的指针,若没有则为首元结点的指针
头结点的作用:
增加头结点方便首元结点的处理
则对链表的第一个数据元素的操作与其他元素相同
便于空表非空表的统一处理
若没有头结点,若链表为空,则首元结点没有,则指向首元结点的头指针L为空判断条件:LNULL
若存在头结点,无论链表是否为空,头指针都是指向头结点的非空指针。
若为空表,则头指针的指针域为空
L->nextNULL
#include
using namespace std;
#include
#define ok 1
#define error 0
typedef int ElemType;
typedef int status;
//单链表的存储结构
typedef struct LNode{
ElemType data;//结点的数据域
struct LNode *next; //结点的指针域
}LNode,*LinkList; //定义结构体变量。这俩实际上一样都是变量
//就是*LinkList定义变量的时候 比如说指针,不用带*
LinkList p;
//单链表的初始化
status InitList(LinkList &L){
L=new LNode;//生成新节点作为头结点,用头指针指向头结点
L->next=NULL;
return ok;
}
//单链表的取值
status GetElem(LinkList L,int i,ElemType e){
p=L->next;int j=1;
while (p&&j<i){
p=p->next;
j++;
}
if(!p||j>i)return error;//i值不合法,i>n或i<=0
e=p->data;
return ok;
}
LNode*LocateElem(LinkList L,ElemType e){
p=L->next;
while(p&&p->data!=e)
p=p->next;
return p;//查找成功返回值为e,查找失败p为NULL
}
//插入
status ListInsert(LinkList &L,int i,ElemType e){
LinkList s;
p=L;int j=0;
while(p&&j<i-1)
{
p=p->next;
j++;}
if(!p||j>i-1)return error;//i>n+1或i<1
s=new LNode;
s->data=e;
s->next=p->next;
p->next=s;
return ok;
}
//删除
status ListDelete(LinkList &L,int i){
LinkList q;
p=L;//头结点
int j=0;
while((j<i-1)&&p->next){
p=p->next;j++;
}
if(!(p->next)||(j>i-1))return error;
q=p->next;
p->next=q->next;
delete q;
return ok;
}
int main(){
int a=3;
LinkList L;
InitList(L);
ListInsert(L,1,2);//往链表里输入数据,可以先用这个插入的函数
ListInsert(L,1,a);
cout<<L->next->data<<endl<<L->next->next->data;
}
简单的测试
用前插法或后插法往链表里添加数据:
前插法就是一直在头结点之后插入,因此第一个插入的等所有数据插入完就成了最后一个。(这是我们应该考虑的)
//前插法创建单链表
void CreateList_H(LinkList &L,int n){
LinkList p;
L=new LNode;
L->next=NULL;//先建立一个带头结点的空链表
for(int i=0;i<n;i++){
p=new LNode;
cin>>p->data;
p->next=L->next;L->next=p;
}
}
后插法
就是在后面插入,就跟排队一样,在链表内的顺序与插入顺序一致
//后插法创建单链表
void CreateList_R(LinkList &L,int n){
LinkList p;
L=new LNode;
L->next=NULL;//先建立一个带头结点的空链表
//采用头指针的思想,弄个尾指针
LinkList r;
r=L;//尾指针指向头结点
for(int i=0;i<n;i++){
p=new LNode;
cin>>p->data;
p->next=NULL;r->next=p; //将新结点*p插入尾结点之后
r=p;//将最后一个结点作为新的尾结点
}
}
前插法具体实现运行案例请点击此处数据结构 单链表头插法
后插法请点击此处尾插法