Linux C 数据结构—链表
一 . 链表特点/结构/分类
链表特点:
优势:链式存储,可以很好的解决以上问题
缺点: 一片连续的空间,成片移动的现象,数据个数固定
链表结构:
每个数据块称之为一个节点,包含: 数据域、指针域。
数据域: 存放数据。
指针域: 存放下一个数据的地址。
链表分类:
单向链表、双向链表、单向循环链表、双向循环链表。
二. 单向链表代码实现
//完整版代码
#include <stdio.h>
#include <stdlib.h>
typedef int data_t;
typedef struct node_t
{
data_t data;
struct node_t * next;
}linknode_t, *linklist_t; // linknode_t * 等价 linklist_t
//创建空链表
linklist_t create_empty_linklist()
{
linklist_t h = (linklist_t )malloc(sizeof(linknode_t ));//sizeof(linknode_t);求结构体的大小,不求指针的大小
h->next = NULL;
return h;
}
//判断链表是否为空
int empty_linklist(linklist_t h)
{
return (h->next == NULL) ;
}
//求链表长度
int length_linklist(linklist_t h)
{
int len = 0;
while(h->next != NULL)
{
len++;
h = h->next;
}
return len;
}
//遍历链表
void visit_linklist(linklist_t h)
{
h = h->next;
while(h != NULL)
{
printf("%d\n",h->data);
h = h->next;
}
return ;
}
//在链表中查找指定元素,返回编号。 若不存在,返回 -1.
int search_linklist(linklist_t h,data_t value)
{
int num = 1;
h = h->next;
while(h != NULL) //如果当前数据不为空,则进循环。
{
if(h->data == value) //如果当前节点的数据是要查找的数据,
{
return num; //那么返回对应的编号。
}
h = h->next; //if 条件为假时,才能执行到此句。 h指向下一个节点。
num ++; //位置 + 1
}
return -1; //while 遍历结束后,还没有执行if中的return,证明没有找到数据,返回-1,表示数据不存在。
}
//获得特定位置的值
int get_linklist(linklist_t h,int pos, data_t *val)
{
if(pos < 1 || pos > length_linklist(h)) //大于长度的话,肯定没有数据可获取。
{
return -1;
}
while(pos--) //while 定位到当前要找的这个特定位置。
h = h->next;
*val = h->data; //取出当前位置的值,赋值给指针所指的变量中。
return 0;
}
//在链表指定位置插入元素,插入成功,返回0,失败返回 -1.
int insert_linklist_1(linklist_t h,data_t value,int pos)
{
if(pos < 1 || pos > length_linklist(h)+1) //可以比长度大1,证明可以插入数据到链表的最后位置上。但是再大就不行了。
return -1;
while(--pos) //定位到当前位置的前一个节点上。
h = h->next;
linklist_t new = (linklist_t )malloc(sizeof(linknode_t )); //插入节点步骤1:创建新节点。
new->data = value; //插入节点步骤2:给新节点赋值。
new->next = h->next;
h->next = new; //插入节点步骤3:将新节点的首地址,赋值给前一个节点的next指针上。
return 0;
}
//按照递增的次序在链表中插入新元素
int insert_linklist_2(linklist_t h,data_t value)
{
while(h->next != NULL) //此 while 目的:找到 value 的插入位置。
{
if(value > h->next->data) //当前的 value 值,与 h下一个节点的数据 做比较。如果value大,继续向后。
h = h->next; //也就是说,希望找到一个 value <= h->next->data 的位置,将 value 插入到 h->next->data 数据的前一个位置上。
else
break;
}
linklist_t new = (linklist_t )malloc(sizeof(linknode_t )); //插入元素步骤1:创建新节点。
new->data = value; //插入节点步骤2:给新节点赋值。
new->next = h->next;
h->next = new; //插入节点步骤3:将新节点的首地址,赋值给前一个节点的next指针上。
return 0;
}
//删除链表中指定位置的元素,删除成功返回0,失败返回 -1 。
int delete_linklist_1(linklist_t h,int pos)
{
if(pos < 1 || pos > length_linklist(h)) //不能大于长度。
return -1;
while(--pos) //找到 要删除的节点 的 前一个位置。
h = h->next;
linklist_t f = h->next;
h->next = f->next; //删除节点步骤1: 将后一个节点搭到前一个节点上。
free(f); //删除节点步骤2: 释放当前位置。
return 0;
}
//删除链表中和指定值相等的所有元素。
int delete_linklist_2(linklist_t h,data_t value)
{
linklist_t f = NULL;
while(h->next != NULL) //找到要删除的当前位置。
{
if(value == h->next->data)
{
f = h->next;
h->next = f->next; //删除节点步骤1: 将后一个节点搭到前一个节点上。
free(f); //删除节点步骤2: 释放当前位置。
}
else
h = h->next;
}
return 0;
}
//除头结点外,释放链表中其他所有节点。
void clear_linklist(linklist_t h)
{
if(empty_linklist(h) == 1) //如果链表已经为空,则不需要再释放。
return ;
linklist_t f = NULL;
while(h->next != NULL) //始终释放头结点的下一个节点,直到头结点的下一个节点为空。
{
f = h->next;
h->next = f->next; //删除节点的步骤1、 步骤2.
free(f);
}
return ;
}
//测试函数
int main()
{
linklist_t h = create_empty_linklist(); //创建空节点。
insert_linklist_1(h,11,1); //按位置插入值。
insert_linklist_2(h,55); //按大小插入值。
insert_linklist_2(h,33);
insert_linklist_2(h,77);
insert_linklist_2(h,77);
insert_linklist_2(h,22);
insert_linklist_2(h,88);
visit_linklist(h); //遍历。
delete_linklist_1(h,4); //按位置删除。
delete_linklist_2(h,77); //按数值删除大小。
int s = 0;
get_linklist(h,4,&s); //到指定位置寻找数值。
printf("s : %d\n",s);
puts("which num you want to search :"); //输入一个要找的值。
scanf("%d",&s);
int pos = search_linklist(h,s);
if(pos == -1) //判断此数值是否存在。
printf("Not found\n");
else
printf("In :%d\n",pos);
visit_linklist(h); //遍历。
clear_linklist(h); //清空链表。
puts("And:");
visit_linklist(h); //遍历
return 0;
}
//链表应用1:
//两个有序表,形成一个新的有序表
//完成功能: 创建空链表、求表长、遍历链表、插入、清空、实现合并、测试函数。
A : 11 33 44 77 88
B : 22 55 66 99
#include <stdio.h>
#include <stdlib.h>
typedef int data_t;
typedef struct node_t
{
data_t data;
struct node_t * next;
}linknode_t, *linklist_t;
linklist_t create_empty_linklist()
{
linklist_t h = (linklist_t )malloc(sizeof(linknode_t ));//sizeof(linknode_t);求结构体的大小,不求指针的大小
h->next = NULL;
return h;
}
int empty_linklist(linklist_t h)
{
return (h->next == NULL) ;
}
int length_linklist(linklist_t h)
{
int len = 0;
while(h->next != NULL)
{
len++;
h = h->next;
}
return len;
}
void visit_linklist(linklist_t h)
{
h = h->next;
while(h != NULL)
{
printf("%d\n",h->data);
h = h->next;
}
return ;
}
int insert_linklist(linklist_t h,data_t value,int pos)
{
if(pos < 1 || pos > length_linklist(h)+1)
return -1;
while(--pos)
h = h->next;
linklist_t new = (linklist_t )malloc(sizeof(linknode_t ));
new->data = value;
new->next = h->next;
h->next = new;
return 0;
}
void clear_linklist(linklist_t h)
{
if(empty_linklist(h) == 1)
return ;
linklist_t f = NULL;
while(h->next != NULL)
{
f = h->next;
h->next = f->next;
free(f);
}
return ;
}
void Merge(linklist_t p,linklist_t q)
{
linklist_t h = p;
p = p->next;
q = q->next;
while(q != NULL && p != NULL) //确保要比较的两个节点,都有数据。
{
if(p->data < q->data) //比较大小,小的在前,大的在后。
{
h->next = p;
p = p->next;
}
else
{
h->next = q;
q = q->next;
}
h = h->next;
}
if(p != NULL) //结束之后再做判断。 将没有结束的那个链表,连接到合并的链表中。形成一个完整的链表。
h->next = p;
else
h->next = q;
return ;
}
int main()
{
linklist_t A = create_empty_linklist();
linklist_t B = create_empty_linklist();
insert_linklist(A,88,1);
insert_linklist(A,77,1);
insert_linklist(A,44,1);
insert_linklist(A,33,1);
insert_linklist(A,11,1);
insert_linklist(A,99,6);
insert_linklist(B,66,1);
insert_linklist(B,55,1);
insert_linklist(B,22,1);
Merge(A,B);
visit_linklist(A);
clear_linklist(A);
return 0;
}
三.单向循环链表实现
将尾节点的指针域,赋值为头结点的地址。 形成一个闭环,就是单项循环链表。
//约瑟夫环代码
#include <stdio.h>
#include <stdlib.h>
typedef struct node_t
{
int data;
struct node_t *next;
}linknode_t;
int main()
{
linknode_t *h = (linknode_t *)malloc(sizeof(linknode_t )); //创建第一个节点
h->data = 1; //数据域中存放编号为1. 不带头结点的链表。
h->next = NULL;
linknode_t *new = NULL,*q = h,*t = NULL; //new 用来存放新创建的节点。q用来连接节点到链表中,t为要删除的节点。
int n = 8,k = 3,m = 4; //8个人。从第3个人开始,数到4的出列。
int i;
for(i=2; i<=n; i++) //创建其余节点。
{
new = (linknode_t *)malloc(sizeof(linknode_t )); //创建
new->data = i; //赋值。
new->next = NULL;
q->next = new; //连接到链表当中。
q = new; //q 指向下一个节点。
}
q->next = h; //尾节点指针 指向 头节点。形成闭环。循环链表。
q = h; //从头开始数数。
for(i=0;i<k-1;i++) //找到起始位置。
{
q = q->next;
}
while(q->next != q) //如果此节点的指针域不指向自己,证明此时有多于一个的节点。
{
for(i=1;i<m-1;i++) //找出 数到4的节点的前一个位置。
{
q = q->next;
t = q->next;
}
printf("%5d",t->data); //出列的节点的编号。
q->next = t->next; //删除节点步骤1.
free(t); //步骤2.
q = q->next; // q 向后移动一位。供下次数数。
}
printf("\nThe end : %d\n",q->data); //剩下的最后一个节点。
free(q);
return 0;
}
四. 双向链表:
typedef struct node_t
{
char name[20]; //数据域。
struct node_t *prev;
struct node_t *next; //指针域。
}linknode_t;
好处: 可以双向访问每个节点。
操作:
1)插入一个节点 : //当前新创建的节点称为q,插入位置的下一个节点称为p 。
a. q->prev = p->prev; //前两步是给新节点赋值,后两步是将前后两个节点的指针,调整指向新节点。
b. q->next = p;
c. p->prev->next = q;
d. p->prev = q;
2)删除一个节点 ://要删除的节点是 q
a. (q->prev)->next = q->next;
b. (q->next)->prev = q->prev;
c. free(q);
四.用链表实现多项式合并
A16(X)=5+2X+8X8+3X16, 相应的线性表:A((5,0),(2,1),(8,8),(3,16))
B8(X)=6X+23X6–8X8, 相应的线性表:B((6,1),(23,6),(-8,8))
A+B 的结果多项式C为:
C16(X)= 5 + 8X + 23X6 + 3X16 ,相应的线性表:C((5,0),(8,1),(23,6),(3,16))
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
float coef; //系数
int exp; //指数
} data_t;
typedef struct node_t
{
data_t data;
struct node_t *next;
}linknode_t, *linklist_t;
linklist_t create_empty_link()
{
linklist_t h = (linklist_t )malloc(sizeof(linknode_t));
h->next = NULL;
return h;
}
void insert_linklist(linklist_t s,data_t value)
{
while(s->next != NULL)
s = s->next;
linklist_t new = (linklist_t)malloc(sizeof(linknode_t));
new->data = value;
new->next = NULL;
s->next = new;
return ;
}
void clear_linklist(linklist_t s)
{
linklist_t q = NULL;
while(s->next != NULL)
{
q = s->next;
s->next = q->next ;
free(q);
}
return ;
}
linklist_t Merge_linklist(linklist_t s1,linklist_t s2)
{
linklist_t C = create_empty_link();
linklist_t new = C;
s1 = s1->next;
s2 = s2->next;
while( s1 && s2 )
{
if( s1->data.exp < s2->data.exp )
{
insert_linklist(new,s1->data);
new = new->next;
s1 = s1->next;
}
else if( s1->data.exp > s2->data.exp )
{
insert_linklist(new,s2->data);
new = new->next;
s2 = s2->next;
}
else
{
float sum = s1->data.coef + s2->data.coef;
if( sum )
{
data_t v = {sum,s1->data.exp};
insert_linklist(new,v);
new = new->next;
s1 = s1->next;
s2 = s2->next;
}
else
{
s1 = s1->next;
s2 = s2->next;
}
}
}
if( s1 != NULL )
new->next = s1;
else
new->next = s2;
return C;
}
int main()
{
linklist_t A = create_empty_link();
linklist_t B = create_empty_link();
data_t ddd ;
puts("Put A:coef exp :");
while(1)
{
scanf("%f%d",&ddd.coef,&ddd.exp);
if(ddd.coef == 0)
break;
insert_linklist(A,ddd);
}
puts("Put B:coef exp :");
while(1)
{
scanf("%f%d",&ddd.coef,&ddd.exp);
if(ddd.coef == 0)
break;
insert_linklist(B,ddd);
}
linklist_t result = Merge_linklist(A,B);
while(result->next != NULL)
{
result = result->next;
printf("%.2f %d\n",result->data.coef,result->data.exp);
}
clear_linklist(A);
clear_linklist(B);
clear_linklist(result);
return ;
}
//此代码的局限性: 创建第三个链表,没有考虑内存空间使用问题。还有 Merge_linklist 函数,调用其他函数,独立性差。