今天是元气满满的第四天,今天会有上下两篇,上篇写线性表的链式存储结构,下篇写有关栈的概述(emmmm现在已经很晚了,估计明天才会有下篇)。
一、单项链表的概述
将线性表L中各元素分布在存储器的不同存储快,称为结点,通过抵制或指针建立它们之间的联系,所得到的存储结构为链表结构。
结点data域存放数据元素ai,而next域是一个指针,指向ai的直接后继ai+1所在的结点。
二、单项链表的基本运算
单链表的基本运算主要包括单链表的建立和查找,插入和删除。
头文件linklist.h
/*
作者:张小五
时间:2018年1月28日
*/
#ifndef LINKLIST_H_
#define LINKLIST_H_
typedef struct node{ //结点类型
int data; //结点的数据域
struct node *next; //结点元素的后继指针域
}linkNode,*linkp;
void CreateList(linkp H); //建立单链表
void PrintList(linkp H);//打印单链表
linkp GetList(linkp H,int i);//根据序号查找
void InsertList(linkp H,int x ,int i);//插入元素
void DeleteList(linkp H,int i);//删除元素
#endif // LINKLIST_H_
linklist.c
/*
作者:张小五
时间:2018年1月28日
*/
#include "stdio.h"
#include "linklist.h"
//建立单链表
void CreateList(linkp H){
int i=1;
int a;
linkp P,r;
r=H; //首先定义一个数据区为空的头结点,将头结点指向第一个带有元素的结点
printf("请输入链表第%d个元素,以-1结束:",i);
scanf("%d",&a);
while(a!=-1){
P=(linkp)malloc(sizeof(linkNode));
P->data = a;
r->next = P; //指针指向下一个结点
r = P;
i++;
printf("请输入链表第%d个元素,以-1结束:",i);
scanf("%d",&a);
}
r->next=NULL; //循环到最后一个结点时,最后一个结点指向NULL,这是单项循环链表的特点
}
//打印单链表
void PrintList(linkp H){
linkp P;
P = H->next;
printf("链表打印为:\n");
while(P->next!=NULL){
printf("%d ",P->data);
P = P->next;
}
printf("%d ",P->data);
printf("\n");
}
//根据序号查找
linkp GetList(linkp H,int i){
int j = -1;
linkp P=H;
if(i<0){
return(NULL);
}
while(P->next&&j<i){
P =P->next;
j++;
}
if(i==j){
return(P);
}else{
return(NULL);//查找失败
}
}
//插入链表
void InsertList(linkp H,int x ,int i){
linkp p,q;
if(i==0){
p=H;
}else{
p = GetList(H,i-1);//取结点ai-1
}
if(p==NULL){
printf("位置不存在!\n");
}else{
q=(linkp)malloc(sizeof(linkNode));
q->data = x; //存入数据
q->next = p->next;
p->next = q;
}
}
//删除结点
void DeleteList(linkp H,int i){
linkp p,q;
if(i==0){
p=H;
}else{
p = GetList(H,i-1);//取结点ai-1
}
if(p!=NULL&&p->next!=NULL){
q = p->next;
p->next = q->next;
free(q);
}else{
printf("删除失败!\n");
}
}
main.c
/*
作者:张小五
时间:2018年1月28日
*/
#include "stdio.h"
#include "linklist.h"
#include "linklist.c"
int main(int argc,char *argv[]){
linkp H;
H =(linkp)malloc(sizeof(linkNode));
H->next = NULL;
CreateList(H);
PrintList(H);
/*int i;
linkp temp;
printf("请输入要查找的位置:");
scanf("%d",&i);
temp = GetList(H ,i-1);
if(temp!=NULL){
printf("你要查找的数值是%d\n",temp->data);
}else{
printf("你要查找的位置不存在!\n");
}*/
/*int x,i;
printf("请输入要插入的数据和位置:\n");
scanf("%d %d",&x,&i);
InsertList(H,x,i-1);*/
int i ;
printf("请输入要删除的位置:");
scanf("%d",&i);
DeleteList(H,i-1);
PrintList(H);
return 0;
}
运行结果(只显示删除的运行结果):
三、单项链表的扩展运算
有关单项列表的扩展运算无非是列表的倒置以及合并,首先来看链表的倒置:
//倒置链表
void L1n_Ln1(linkp H){
linkp p,q;
p = H->next; //将头结点复制给p然后将头结点置空
H->next = NULL;
while(p!=NULL){
q = p;//复制一份结点,让结点q代替结点p进行链接
p = p->next;
q->next = H->next;
H->next = q;//将结点插入到头结点中
}
}
结果显而易见:
两个有序链表之间的有序合并:例如一个链表L1=(1,3,5),另一个链表L2=(2,4)则合成新链表L=(1,2,3,4,5)代码就不在这里进行展示了,但是说一下原理。
首先用他们只需一个头结点就可以了,所以可以释放一个头结点,用一个复制的结点来代替头结点,然后两个链表的元素开始比较大小,较小的这个放到新的头结点之后,然后较小的那列结点后移,然后再次比较,直至两个链表都为空停止。如图:
四、单项循环链表的概述及运算
单项循环链表是单链表的一种改进,若将单链表的首尾结构相连,便构成了单项循环链表结构,这样,若运算频繁在为不运行,可设一表尾指针R(H就可以省去了)。这样,为获得表位an-1,取R->data即可,不需要遍历到表尾,而取a0的运算为(R->NEXT->NEXT)->data
运算方式也和单向链表大致相同,唯一不同的计算为两链表进行简单连接,设ra和rb分别为两单项循环链表尾指针。
这样就可以完成AB的简单链接。
五、双向循环链表的概述和计算
在单链表L中,查找ai的后继next(L,ai),耗时仅为O(1),因为取ai的后继指针酒可以。但是查找ai的直接前驱Piroi(L,ai),则需要从链表头指针开始,他的时间复杂度是O(n)。另外,若链表中有一指针被破坏,则整个链表脱节。这是单链表的不足,为此,引入双向链表。先定义双向链表中的结点:
那么双向循环链表和单向链表的计算又会有差异,我就不贴源代码在这里了,但是我会把算法放在这里,有时间的话我会写一个练习篇放在那里面:
下面是双向循环链表的插入算法:
下面是双向循环链表的删除算法: