文章目录
链式存储
最后一句表明,k2是k1的后继结点,k3是k2的后继结点,k4是k3的后继结点,k5是k4的后继结点,k5是空的表
单链表
typedef int datatype;//简化算法,数据类型用整形表示
typedef struct link_node{
datatype info;//整型数据域
struct link_node *next;//指针域,指向本结点类型指针
}node;//取名node
typedef node *linklist;//指向结构体类型的指针取名linklist
建立一个空的单链表
赋值
node *init()
{
return NULL;
}
int main()
{
linklist head;
head=init();
}
传递指针地址
void init(linklist *p)//*p指向linklist这种类型的指针
{
*p=NULL;//将链表指针赋值为空
}
int main()
{
linklist head;
init(&head);
}
输出单链表中各个结点的值
void display(node *head)//也可以是linklist head
{
node *p;
p=head;//初始值指向单链表第一个结点
if(!p) printf("/n单链表是空的!");
else
{
printf("\n单链表各个结点值为:")
while(p)
{
printf("%5d",p->info);
p=p->next;//p赋值为p的next
}
}
}
- 时间复杂度O(n)
在单链表中查找第i个结点
node *find(node *head,int i)
{
int j=1;
node *p=head;
if(i<1) return NULL;
while (p&&i!=j){
p=p->next;
j++;
}
return p;
}
若i不存在,返回的是空值
单链表插入
最前面插入值为x的新结点
- 申请一个结点的空间,让指针指向他
p=(linklist)malloc(sizeof(node));
p->info=x
p->next=head
p->next暂时为headhead=p
head重新指向这个链表
某一个结点后面插入新结点
- 申请一个结点的空间,让指针指向他
p=(linklist)malloc(sizeof(node));
p->info=x
p->next=q->next
q->next=p
算法:在单链表第i个结点后插入一个值为x的新结点
linklist insert(node *head,datatype x,int i)
{
node *p,*q;
q=find(head,i);
if(!q&&i!=0)//找到的位置不存在或者i不合法
printf("找不到%d个结点,不能插入%d",i,x);
else{
p=(linklist)malloc(sizeof(node));
p->info=x
}
if(i==0){
p->next=head;
head=p
}
else{
p->next=q->next;
q->next=p;
}
return head;
}
头插法建立单链表
建立单链表
linklist creatbystack()
{ linklist head,s;
datatype x;
head=NULL;
printf("请输入若干整数序列:\n");
scanf("%d",&x);
while (x!=0) /*以0结束输入*/
{ s=(linklist)malloc(sizeof(node)); /*生成待插入结点*/
s->info=x;
s->next=head; /*将新结点插入到链表最前面*/
head=s;
scanf("%d",&x);
}
return head; /*返回建立的单链表*/
}
输出单链表
void print(linklist head)
{ linklist p;
int i=0;
p=head;
printf("List is:\n");
while(p)
{
printf("%5d",p->info);
p=p->next;
i++;
if (i%10==0) printf("\n");
}
printf("\n");
}
在尾部建立单链表
关键一步设置一个链尾指针r,每次将新的结点链在r的后面,使其成为新的表尾结点
linklist creatbyqueue()
{
linklist head,r,s;//node *head,*s,*r;
datatype x;
head=NULL;r=NULL;//表首和表尾指针为空
scanf("%d",&x);
while (x!=0)
{
s=(linklist)malloc(sizeof(node));
s->info=x;
if(head==NULL) head=s//原链表为空,head指向链表第一个结点
else r->next=s;//将s指向的结点加到r指向结点的后面
r=s;
scanf("%d",&x);
}
if(r!=NULL)
r->next=NULL;
return head;
}
单链表删除
删除单链表最前面的结点
-
p=head
保留删除的结点
-
head=p->next
head指向链表的第二个结点
-
释放删除结点的空间
删除链表非首结点
- 找到被删除结点的前驱结点
pre->next=p->next
删除p指示结点
- 释放被删除结点空间
free(p)
算法:在单链表中删除一个值为x的结点
linklist dele(node *head,datatype x)
{
node *pre=NULL,*p;//不仅要知道p的位置,还要知道p前面的位置
if(!head){
printf("单链表是空的");
return head;
}
p=head;
while(p&&p->info!=x)//p不为空且查找的值不是x
//&&条件不能颠倒,否则视为程序非法访问内存
{
pre=p;//pre指向p
p=p->next//p往下走
}
if(p!=NULL)
{
if(!pre)
head=head->next//head指向下一个结点
}else{
pre->next=p->next;
}
free(p);
}
若链表有多个x结点,只能删除第一个结点
链式存储结构优缺点
课后答疑
- 对下面代码的了解
typedef int datatype;//简化算法,数据类型用整形表示
typedef struct link_node{
datatype info;//整型数据域
struct link_node *next;//指针域,指向本结点类型指针
}node;//取名node
typedef node *linklist;//指向结构体类型的指针取名linklist
node=link_node
node*=linklist=link_node*
指向结构体的指针类型
-
建立空链表两种代码的理解
node*
表示要返回node*定义的指针,那么应该在main函数里面有个专门的变量去接收返回值。所以在函数里面不需要return返回任何值!
这里void表示没有返回值,也就是说函数里面需要返回值,那么我们想要直接更改函数里的值,即main函数会跟着形参改变,我们就该传一个地址(地址传递)。这个时候就该返回return head等等了 -
对
p=p->next
的理解
假设前两个单元为k1,k2,让p指向k1,那么p->next是在k1中,里面保留了k2的地址 -
由上可知,链表最后一个值一定要
赋值为空
-
删除链首结点 对
head=head->next理解
在没有头结点的情况下,head就是k1,head->next就是k1->next,即保留k2结点 -
头插法对链表就地倒置特别注意:
前面head=NULL
为什么呢?因为在头插法中,最先进来的最后输出,当head赋值为空时,第一个进来的s 要s->next=head,即s的next指针域为空。那么当链表输出中,只有遇到空值,链表才结束。 -
链表结束后,head,linklist定义的pre,p等等都会被
自动
释放掉,因为这属于动态分配内存,pre,p,head类似局部变量 -
但是由malloc函数分配的内存,就像k1,k2,k3等,需要
free
来释放 -
链表删除,插入,都不改变其他结点的位置,则其时间复杂度和空间复杂度为O(1)