目录
(4)双向循环链表的查找 (5)定位:查找第 i 个结点在链中的位置
(6)双向循环链表的插入 (非空表) (7)双向循环链表的插入 (空表)
(2)存储密度=结点数据本身所占的存储量/结点结构所占的存储总量
一、线性表
1、定义
- 定义n个数据元素的有限序列,记作( a1, a2,..., an ) ai是表中数据元素,n是表长度
2、特点
- 除第一个元素外,其他每一个元素有一个且仅有一个直接前驱(前面紧挨着)。
- 除最后一个元素外其他每一个元素有一个且仅有一个直接后继(后面紧挨着)。
3、数组——静态线性表
#include <stdio.h>
#define MAX 10
typedef int elementtype; //元素定义
//打印操作
void printf(elementtype *a)
{
int i;
for(i=0;i<MAX;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
int main()
{
elementtype a[MAX]={0}; //初始化
print(a);//对数据进行遍历(顺序访问)
return 0;
}
二、顺序表
1、定义
- 定义将线性表中的元素相继存放在一个连续的存储空间中。
- 可利用一维数组描述存储结构
2、特点
- 线性表的顺序存储方式。
3、遍历方式
- 顺序访问,可以随机存取(顺序访问效率高)
三、链表(链接表是线性表的链接存储表示)——处理数据
1、特点
- 每个元素(表项)由结点(Node)构成。
data:存储数据元素
next:存储指向后继结点的位置
- 线性结构
first——头指针
a0| →a1| →——值域|指针域(指针域内放下一结点的地址)→值域|指针域
- 结点可以连续,可以不连续存储
- 结点的逻辑顺序与物理顺序可以不一致(物理上不连续,箭头表示的是逻辑顺序)
- 表可扩充
(2)准备工作
①基本的头文件
使用链表时,首先应包含一些基本的头文件,因为涉及到内存的操作和字符串的操作。
#include "stdio.h"
#include "stdlib.h" //提供malloc()和free()
#include "string.h" //提供strcpy()等
②malloc函数
其函数原型如下:
void *malloc(unsigned int size);
这个函数返回的是个void类型指针,所以在使用时应注意强制类型转换成需要的指针类型。
③free函数
其函数原型如下:
void free(void *p);
这个函数是用来释放指针p作指向的内存区。
④创建节点(结构体)
struct Node
{
int a; //数据域
struct Node* next; //指针域(指向节点的指针)
};
⑤全局定义链表头尾指针,方便调用
struct Node* head= NULL;
struct Node* end = NULL;
2、单链表
(1)定义——单链表的结点结构
typedef char ListData;
typedef struct node{ //链表结点
ListData data; //结点数据域
struct node* link; //结点链域(指针域)
}ListNode;
typedef ListNode* LinkList;
LinkList first; //链表头指针
(2)基本操作——无表头
Ⅰ:增(插入)
①在第一个结点前插入(头插)
void insert_node_head(Link * head,Link new_node)
{
new_node->next=*head;
*head=new_node;
}
#include <stdio.h>
#include <stdlib.h>
#define MALLOC_OK 1
#define MALLOC_NO 0 //分配成功与否
#define CREATE_OK 1
#define CREATE_NO 0 //创建成功与否
struct node
{
int num;
char name[20];
char sex;
int age;
char add[100];
struct node * next; //指向后一位,形成链式结构
};
typedef struct node Node;
typedef Node *Link;
//创建链表
void create_link(Link * head)
{
*head=NULL; //链表为空
}
//创建结点
int is_malloc_ok(Link new_node)
{
if(NULL == new_node)
{
printf("malloc error!\n");
return MALLOC_NO;
}
return MALLOC_OK;
}
int create_node(Link * new_node)
{
*new_node=(Link)malloc(sizeof(Node));
if(MALLOC_OK == is_malloc_ok(*new_node))
{
return CREATE_OK;
}
return CREATE_NO;
}
//头插
void insert_node_head(Link * head,Link new_node)
{
new_node->next=*head;
*head=new_node;
}
//显示链表结果
void display_link(Link head)
{
Link p=NULL;
p=head;
if(NULL==head)
{
printf("The link is empty!\n"); //显示空链表
return;
}
while(p!=NULL)
{
printf("%d\n",p->num);
p=p->next;
}
}
//释放结点
void release_link(Link * head)
{
Link p=NULL;
p=*head;
while(*head!=NULL)
{
*head=(*head)->next;
free(p);
p=*head;
}
}
//创建链表,创建头指针以便找到链表,是否需要二级指针,根据是否要带回指针值为判断依据
int main()
{
Link head=NULL; //头指针,防止野指针
Link new_node=NULL;
int i;
create_link(&head);
for(i=0;i<10;i++)
{
if(CREATE_OK == create_node(&new_node))
{
new_node->num = i+1;
insert_node_head(&head,new_node);
}
}
display_link(head);
release_link(&head);
display_link(head);
return 0;
}
②在最后一个结点前插入(尾插)
void insert_node_tail(Link * head,Link new_node)
{
Link p = NULL;
p=*head;
if(*head == NULL) //链表为空时
{
*head=new_node;
new_node->next = NULL;
}
else
{
while(p->next!= NULL)
{
p=p->next;
}
p->next=new_node;
new_node->next = NULL;
}
}
#include <stdio.h>
#include <stdlib.h>
#define MALLOC_OK 1
#define MALLOC_NO 0 //分配成功与否
#define CREATE_OK 1
#define CREATE_NO 0 //创建成功与否
struct node
{
int num;
char name[20];
char sex;
int age;
char add[100];
struct node * next; //指向后一位,形成链式结构
};
typedef struct node Node;
typedef Node *Link;
//创建链表
void create_link(Link * head)
{
*head=NULL; //链表为空
}
//创建结点
int is_malloc_ok(Link new_node)
{
if(NULL == new_node)
{
printf("malloc error!\n");
return MALLOC_NO;
}
return MALLOC_OK;
}
int create_node(Link * new_node)
{
*new_node=(Link)malloc(sizeof(Node));
if(MALLOC_OK == is_malloc_ok(*new_node))
{
return CREATE_OK;
}
return CREATE_NO;
}
//尾插
void insert_node_tail(Link * head,Link new_node)
{
Link p = NULL;
p=*head;
if(*head == NULL) //链表为空时
{
*head=new_node;
new_node->next = NULL;
}
else
{
while(p->next!= NULL)
{
p=p->next;
}
p->next=new_node;
new_node->next = NULL;
}
}
//显示链表结果
void display_link(Link head)
{
Link p=NULL;
p=head;
if(NULL==head)
{
printf("The link is empty!\n"); //显示空链表
return;
}
while(p!=NULL)
{
printf("%d\n",p->num);
p=p->next;
}
}
//释放结点
void release_link(Link * head)
{
Link p=NULL;
p=*head;
while(*head!=NULL)
{
*head=(*head)->next;
free(p);
p=*head;
}
printf("link is empty!\n");
}
//创建链表,创建头指针以便找到链表,是否需要二级指针,根据是否要带回指针值为判断依据
int main()
{
Link head=NULL; //头指针,防止野指针
Link new_node=NULL;
int i;
create_link(&head); //创建链表,取回头结点的地址所以要用到二级指针,但并不是定义二级指针而是取一级指针的地址
for(i=0;i<10;i++)
{
if(CREATE_OK == create_node(&new_node))
{
new_node->num = i+1;
insert_node_tail(&head,new_node);
}
}
display_link(head);
release_link(&head);
display_link(head);
return 0;
}
③随机插入法
newnode->link=p->link;
p->link=newnode;
a、前插
void insert_node_mid_before(Link *head,Link new_node,int loc)
{
Link p=NULL;
Link q=NULL;p=q=*head;
while(p != NULL && p->num !=loc)//里面的条件顺序不能颠倒,会出现段错误,因为当p=NULL时,p没有num,会出现段错误,(p=NULL→走完链表)或(p->num=loc找到位置)
{
q=p;
p=p->next;
}
if(p = *head)
{
new_node->next = *head;//************
*head = new_node;
}
else
{
q->next=new_node;
new_node->next = p;
}
}
#include <stdio.h>
#include <stdlib.h>
#define MALLOC_OK 1
#define MALLOC_NO 0
#define CREATE_OK 1
#define CREATE_NO 0
struct node
{
int num;
int age;
char name[20];
char add[100];
struct node *next;
};
typedef struct node Node;
typedef Node *Link;
//创建链表
void create_link(Link *head)
{
*head=NULL;
}
//创建结点(检查动态内存分配和创建成功与否)
int is_malloc_ok(Link new_node)
{
if(NULL == new_node)
{
printf("malloc error!\n");
return MALLOC_NO;
}
return MALLOC_OK;
}
int create_node(Link *new_node)
{
*new_node=(Link)malloc(sizeof(Node));
if(MALLOC_OK == is_malloc_ok(*new_node))
{
return CREATE_OK;
}
return CREATE_NO;
}
//插入(首先尾插得到链表,然后前插)
void insert_node_tail(Link *head,Link new_node)
{
Link p=NULL;
p=*head;
if(*head==NULL)
{
*head=new_node;
new_node->next=NULL;
}
else
{
while(p->next != NULL)
{
p=p->next;
}
p->next=new_node;
new_node->next = NULL;
}
}
void insert_node_mid_before(Link *head,Link new_node,int loc)
{
Link p=NULL;
Link q=NULL;
p=q=*head;
while(p != NULL && p->num !=loc)//里面的条件顺序不能颠倒,会出现段错误,因为当p=NULL时,p没有num,会出现段错误
{
q=p;
p=p->next;
}
if(p == *head)
{
new_node->next = *head;
*head = new_node;
}
else
{
q->next=new_node;
new_node->next = p;
}
}
//显示链表内容
void display_link(Link head)
{
Link p=NULL;
p=head;
if(NULL==head)
{
printf("The link is empty!\n");
return;
}
while(p!=NULL)
{
printf("%4d",p->num);
p=p->next;
}
printf("\n");
}
//释放
void release_link(Link *head)
{
Link p=NULL;
p=*head;
while(*head!= NULL)
{
*head=(*head)->next;
free(p);
p=*head;
}
}
int main()
{
Link head=NULL;
Link new_node=NULL;
int i;
int loc;
create_link(&head);
for(i=0;i<10;i++)
{
if(CREATE_OK == create_node(&new_node))
{
new_node->num=i+1;
insert_node_tail(&head,new_node);
}
}
printf("input location\n");
scanf("%d",&loc);
if(CREATE_OK == create_node(&new_node))
{
printf("please input num:\n");
scanf("%d",&new_node->num);
insert_node_mid_before(&head,new_node,loc);
}
display_link(head);
release_link(&head);
display_link(head);
return 0;
}
b、后插
void insert_node_mid_after(Link *head,Link new_node,int loc)
{
Link p=*head;if(NULL == *head)
{
*head=new_node;
new_node->next = NULL;
}
else
{
while(p->next != NULL && p->num != loc)
{
p=p->next;
}
new_node->next = p->next;
p->next = new_node;
}
}
#include <stdio.h>
#include <stdlib.h>
#define MALLOC_OK 1
#define MALLOC_NO 0
#define CREATE_OK 1
#define CREATE_NO 0
struct node
{
int num;
int age;
char name[20];
char add[100];
struct node *next;
};
typedef struct node Node;
typedef Node *Link;
//创建链表
void create_link(Link *head)
{
*head=NULL;
}
//创建结点(检查动态内存分配和创建成功与否)
int is_malloc_ok(Link new_node)
{
if(NULL == new_node)
{
printf("malloc error!\n");
return MALLOC_NO;
}
return MALLOC_OK;
}
int create_node(Link *new_node)
{
*new_node=(Link)malloc(sizeof(Node));
if(MALLOC_OK == is_malloc_ok(*new_node))
{
return CREATE_OK;
}
return CREATE_NO;
}
//插入(首先尾插形成链表,然后后插)
void insert_node_tail(Link *head,Link new_node)
{
Link p=NULL;
p=*head;
if(*head==NULL)
{
*head=new_node;
new_node->next=NULL;
}
else
{
while(p->next != NULL)
{
p=p->next;
}
p->next=new_node;
new_node->next = NULL;
}
}
void insert_node_mid_after(Link *head,Link new_node,int loc)
{
Link p=*head;
if(NULL == *head)
{
*head=new_node;
new_node->next = NULL;
}
else
{
while(p->next != NULL && p->num != loc)
{
p=p->next;
}
new_node->next = p->next;
p->next = new_node;
}
}
//显示链表内容
void display_link(Link head)
{
Link p=NULL;
p=head;
if(NULL==head)
{
printf("The link is empty!\n");
return;
}
while(p!=NULL)
{
printf("%4d",p->num);
p=p->next;
}
printf("\n");
}
//释放
void release_link(Link *head)
{
Link p=NULL;
p=*head;
while(*head!= NULL)
{
*head=(*head)->next;
free(p);
p=*head;
}
}
int main()
{
Link head=NULL;
Link new_node=NULL;
int i;
int loc;
create_link(&head);
for(i=0;i<10;i++)
{
if(CREATE_OK == create_node(&new_node))
{
new_node->num=i+1;
insert_node_tail(&head,new_node);
}
}
printf("old link\n");
display_link(head);
printf("input location\n");
scanf("%d",&loc);
create_node(&new_node);
printf("input new_node->num\n");
scanf("%d",&new_node->num);
insert_node_mid_after(&head,new_node,loc);
display_link(head);
release_link(&head);
display_link(head);
return 0;
}
Ⅱ:删(删除)
①删除尾
void delete_Tail(Link *head )
{
Link p=NULL;
p=*head;
if (NULL == *head)
{
printf("链表为空,无需删除\n");
return;
}
//链表不为空
//链表有一个节点
if (head==p)
{
free();
head=NULL;
;
}
else
{
//找到尾巴前一个节点
struct Node* temp =head;
while (temp->next!=end)
{
temp = temp->next;
}
//找到了,删尾巴
//释放尾巴
free(p);
//尾巴迁移
p->next=NULL;
//尾巴指针为NULL
end->next=NULL;
}
}
②删除头
③删除指定节点
④全部删除——清空release
void release_link(Link *head)
{
Link p=NULL;
p=*head;
while(*head != NULL)
{
*head=(*head)->next;
free(p);
p=*head;
}
}
int delete_link(Link *head,int i)
{
Link p=NULL;
LInk q=NULL;
if(i==1)
{
q=head;
head=
}
}
Ⅲ:改()
Ⅳ:查
①查所有(遍历——display)
void display_link(Link head)
{
Link p=NULL;
p=head;
if(NULL == head)
{
printf("The link is empty!\n");
return ;
}
while(p!=NULL)
{
printf("%d\n",p->num);
p=p->next;
}
}
②查长度(链表长度)
int length_link(Link head)
{
int j=0;
Link p = NULL;
p=head;
if(NULL == head)
{
printf("The length of the link is zero!\n");
return 0;
}
while (p!=NULL)
{
p = p->next;
j++;
}
return j;
}
③查指定结点
(3)基础操作——有表头
有表头单链表,顾名思义,就是创建的链表中表头数据为NULL,next直接指向下一结构体。它包含的基础功能有:添加信息,查找信息,删除信息,信息排序,文件操作,信息显示。
头结点的单链表中,head指示的是所谓的头结点,它不是实际的结点,不是用来储存数据的。可以这样理解,头结点牺牲了一个储存单元,来化简代码,因为头不可能为空了。或者用来存储一些全局量,比如链表长度,这要依具体需求而定。
#include <stdio.h>
#include <stdlib.h>
struct hnode
{
int num;
struct hnode * next;
};
typedef struct hnode Hnode;
typedef Hnode * Hlink;
//创建带表头的链表一
void create_new_node(Hlink * new_node)
{
*new_node =(Hlink)malloc(sizeof(Hnode));
}
//创建带表头的链表二
void create_hlink(Hlink * head)
{
create_new_node(head);
(*head)->next = NULL;
}
//头插
void insert_node_head(Hlink head,Hlink new_node)
{
new_node->next = head->next;
head->next = new_node;
}
//尾插
void insert_node_tail(Hlink head,Hlink new_node)
{
Hlink p= NULL;
p=head;
while(p->next != NULL)
{
p=p->next;
}
p->next =new_node;
new_node->next =NULL;
}
//前插
void insert_node_mid_before(Hlink head,Hlink new_node,int loc)
{
Hlink p=NULL;
Hlink q=NULL;
q=head;
p=head->next;
while(p !=NULL && p->num != loc)
{
q=p;
p=p->next;
}
q->next=new_node;
new_node->next=p;
}
//后插
void insert_node_mid_after(Hlink head,Hlink new_node,int loc)
{
Hlink p=NULL;
p=head;
while(p->next != NULL && p->num !=loc)
{
p=p->next;
}
new_node->next=p->next;
p->next=new_node;
}
//遍历
void display_link(Hlink head)
{
Hlink p=NULL;
if(NULL==head)
{
printf("No such link!\n");
}
else if(NULL == head ->next)
{
printf("Link is empty!\n");
}
else
{
p=head->next;
while(p != NULL)
{
printf("%d\n",p->num);
p=p->next;
}
}
}
//清空
void make_empty(Hlink head)
{
Hlink p=NULL;
p=head->next;
while(p!=NULL)
{
head->next =p->next;
free(p);
p=head->next;
}
}
int main()
{
Hlink head = NULL;
Hlink new_node = NULL;
int i;
int loc;
create_hlink(&head);
for(i=0;i<10;i++)
{
create_new_node(&new_node);
new_node->num = i+1;
// insert_node_head(head,new_node);
insert_node_tail(head,new_node);
}
printf("old Hlink:\n");
display_link(head);
printf("please input location:\n");
scanf("%d",&loc);
create_new_node(&new_node);
printf("please input num:");
scanf("%d",&new_node->num);
//insert_node_mid_before(head,new_node,loc);
insert_node_mid_after(head,new_node,loc);
display_link(head);
make_empty(head);
display_link(head);
return 0;
}
3、静态链表
4、循环链表(带表头的链表实现)
(1)定义
- 循环链表是单链表的变形。
- 循环链表最后一个结点的link 指针不为NULL,而是指向了表的前端。
- 为简化操作,在循环链表中往往加入表头结点。
- 循环链表的特点是:只要知道表中某一结点的地址,就可搜寻到所有其他结点的地址。
(2)定义代码
typedef char ListData;
typedef struct cnode //链表结点
{
ListData data; //结点数据域
struct cnode * link; //结点链域
} CircListNode;
typedef CircListNode * CircLinkList; //循环链表头指针
CircLinkList first; //循环链表头指针
(3)循环链表的按值查找
CircListNode * Find ( CircLinkList first,ListData value )
{ //在链表中从头搜索其数据值为value的结点
CircListNode * p = first->link; //检测指针 p 指示第一个结点
while ( p != first && p->data != value )
{
p = p->link;
}
return p;
}
5、双向链表(带表头的链表实现)
(1)定义
- 双向链表是指在前驱和后继方向都能游历(遍历)的线性链表。
- 双向链表每个结点结构:
- 双向链表通常采用带表头结点的循环链表形式。
(2)定义代码
typedef int ListData;
typedef struct dnode
{
ListNode data; //数据
struct dnode * prior, * next; //指针
} DblNode;
typedef DblNode * DblList; //双向链表
void CreateDblList ( DblList & first )
{
first = ( DblNode * ) malloc( sizeof ( DblNode ) );
if ( first == NULL )
{
printf ( “存储分配错!\n” );
exit (1);
}
first->prior = first->next = first; //表头结点的链指针指向自己
}
(3)双向循环链表的长度
int Length ( DblList first ) //计算带表头结点的双向循环链表的长度
{
DblNode * p = first->next;
int count = 0;
while ( p != first )
{
p = p->next;
count++;
}
return count;
}
(4)双向循环链表的查找
ListNode * Find ( DblList first, ListData x ) //在双向循环链表中搜索含 x 的结点, 若找到, //则返回该结点的地址, 否则返回表头地址。
{
DblNode * p = first->next;
while ( p != first && p->data != x )
{
p = p->next;
}
return p;
}
//也可以在 prior (前驱) 方向查找, 程序类似。
(5)定位:查找第 i 个结点在链中的位置
DblNode * Locate ( DblList first, int i )
{
if ( i < 0 )
{
return first;
}
DblNode * p = first->next;
int j = 1;
while ( p != first && j < i )
{
p = p->next;
j++;
}
return p;
}
//也可以在 prior (前驱) 方向查找, 程序类似。
(6)双向循环链表的插入 (非空表)
(7)双向循环链表的插入 (空表)
int Insert ( DblList first, int i, ListData x )
{
DblNode * p = Locate ( first, i-1 ); //指针定位于插入位置
if ( p == first && i != 1)
{
return 0;
}
DblNode * q = ( DblNode * ) malloc ( sizeof ( DblNode ) ); //分配结点
q->data = x;
q->prior = p;
p->next->prior = q; //在前驱方向链入新结点
q->next = p->next; p->next = q; //在后继方向链入新结点
return 1;
}
(8)双向循环链表的删除
int Remove ( DblList first, int i )
{
DblNode * p = Locate ( first, i ); //指针定位于删除结点位置
if ( p == first )
{
return 0;
}
p->next->prior = p->prior;
p->prior->next = p->next; //将被删结点 *p 从链上摘下
free ( p ); //删去
return 1;
}
四、顺序表与链表的比较
1、基于空间的比较
(1)存储分配的方式
- 顺序表的存储空间是静态分配的
- 链表的存储空间是动态分配的
(2)存储密度=结点数据本身所占的存储量/结点结构所占的存储总量
- 顺序表的存储密度= 1
- 链表的存储密度<1
2、基于事件的比较
(1)存取方式
- 顺序表可以随机存取,也可以顺序存取
- 链表是顺序存取的
(2)插入/删除时移动元素个数
- 顺序表平均需要移动近一半元素
- 链表不需要移动元素,只需要修改指针
- 若插入/删除仅发生在表的两端,宜采用带尾指针的循环链表