存储密度 = (结点数据本身所占的存储量)/(结点结构所占的存储总量)
2.1链表结构定义
链表完整结构:(头指针/头结点)+(首元结点)+NULL
typedef struct LNode{
Elemtype data;
struct LNode *next;
}LNode,*LinkList;
//这里定义用到了递归
链表初始化:
int Initial_LinkList(LinkList &L){
L=new LNode;
if(!L){return -1;}
L.next=NULL;
return 0;
}
int main(){
LinkList L;
Initial_LinkList(L);
}
说明:函数传参什么时候使用指针的引用?
一般来说,用指针做参数,表示把变量的地址传递给子函数,子函数只能修改指针所指变量的值,并不能修改指针的指向。如果想用修改指针的指向,就要用指针的指针,或者指针的引用。指针引用的典型例子:
- 场景一:
主函数中定义了指针但并未申请空间(野指针),要将指针做参数传给子函数,在子函数中申请空间,这时候一定要用指针的引用(因为指针所指的这块内存发生了改变,或者说指针的指向发生了改变)。 - 场景二:
链表做参数时,在遍历,查找操作子函数时,链表不会发生改变,那就用头结点的指针做参数就可以了。但是在增加,修改,删除这种操作时,链表头结点指针所指的这块内存会发生改变,也就是指针的指向可能会发生改变,这种情况下就要头指针的引用。同样在二叉树和图的子函数中如果修改二叉树和图,那就要用指针的引用。 - 实在搞不清楚,就记住: 用引用总是没有问题的。
2.2销毁链表
int Destroy_LinkList(LinkList &L){
LNode *p;
while(L){
p=L;
L=L->next;
delete p;
}
}
2.3清空链表
顺序表清空只要length归零,这是因为顺序表生成后最大长度是一定的,所以只要将标记位归零即可。链表的长度是灵活的,所以清空需要释放除头结点以外其他结点的内存。
int Clear_LinkList(LinkList &L){
LinkList p,q;
p=L->next; //跳过头结点
while(p) //判断是否到表尾
{ q=p->next; delete p; p=q; } //释放后续结点内存空间
L->next=NULL; //头结点指针域置为空
return OK;
}
2.4取值
int Get_LinkList(LinkList L,int n){
LNode *p=L;int i=0;
while(p&&i<n){
i++;
p=p->next;
}
if(!p||i>=n){
return -1;
}else{
return p->data;
}
说明:
1、顺序表的遍历条件是for (i=0;i< L.length;i++),链表的遍历条件是while(p){p=p->next;}
2、链表的长度是未知的,所以在做查找、取值、插入、删除时都可能超出序号范围,且不能预先判断,要在遍历过程中设置判断条件。
2.5循环列表
循环列表与单列表区别是:从循环链表任何一个结点都可以找到其他所有结点,而单链表做不到。循环列表可看成周长变化的环,初始化L->next=L,遍历结束条件p!=L。循环列表可以有头结点或没有,操作上会有区别。
2.6双向链表
双向链表每个结点有两个指针域。双向循环链表是一种特殊情况。
2.7作业
作业1:将两个递增有序链表A、B合并为一个递增有序链表C,要求不另外占用其他存储空间,不允许有重复元素。
/*
思路:
1、C头指针等于A的头指针,p用来保存C的尾结点,初始位置在Lc ;p=Lc=La;
2、比较A、B首元结点pa、pb的大小,如果pa小,将Lc的next指向pa,pa向下一个结点;
if (pa->data<pb->data){p->next=pa;p=pa;pa=pa->next;}
3、如果pb小,将Lc的next指向pb,pb向下一个结点;
if(pa->data>pb->data){p->next=pb;p=pb;pb=pb->next;}
4、如果pa、pb相等,将Lc的next指向pa,pa向下一个结点,删除pb;
if(pa->data=pb->data){p->next=pa;p=pa;pa=pa->next;del=pb;pb=pb->next;delete del;}
5、循环上述2、3、4直到pa或pb等于NULL(链尾);while(pa&&pb){}
6、再把A或B剩下的部分接到C的后面;
p->next=pa?pa:pb;
7、删除B的头结点;delete Lb;
*/
void MergeLinkList(LinkList &La,LinkList &Lb,LinkList &Lc){
LNode *p,*pa=La->next,*pb=Lb->next,*del;
p=Lc=La;
while(pa&&pb){
if (pa->data<pb->data){
p->next=pa;
p=pa;
pa=pa->next;
}else if (pa->data>pb->data){
p->next=pb;
p=pb;
pb=pb->next;
}else{
p->next=pa;
p=pa;
pa=pa->next;
del=pb;
pb=pb->next;
delete del;
}
}
p->next=pa?pa:pb;
delete Lb;
}
作业2:设计一个算法,通过遍历一趟,将链表La中所有结点的链接方向逆转,仍利用原表的存储空间。
int Invert_LinkList(LinkList &L){
LNode *s1,*s2,*p=L,*r;
if(p->next){
r=s1=p=p->next;
}else{return -1;}
if(p->next){
s2=p=p->next;
}else{return -1;}
p=p->next;
while(p){
s2->next=s1;
s1=s2;
s2=p;
p=p->next;
}
s2->next=s1;
L->next=s2;
r->next=NULL;
return 0;
}
作业3:已知长度为n线性表采用顺序存储结构,请写一个算法删除线性表中所有值为item的元素。
void RemoveElem(Sqlist &L){
int item;
printf("请输入要删除的元素:\n");
scanf("%d",&item);
int n=L.length;
int j=0;
for(int i=0;i<n;i++){
if(L.head[i]!=item){L.head[j]=L.head[i];j++;}
}
L.length=j;
}
易错点:
条件语句经常把判断条件“==”写成“=”。