1、链表概念
链表–数据结构–>数据存放的思想
链表是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素,链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
struct Test
{
int data; //数据域
struct Test *next; //指针域
};
int main()
{
struct Test t1 = {1, NULL};
struct Test t2 = {2, NULL};
struct Test t3 = {3, NULL};
t1.next = &t2;
t2.next = &t3;
return 0;
}
2、链表与数组的区别
1)链表是链式的存储结构;数组是顺序的存储结构。
2)链表通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。
3)链表的插入删除元素相对数组较为简单,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难;数组寻找某个元素较为简单,但插入与删除比较复杂,由于最大长度需要再编程一开始时指定,故当达到最大长度时,扩充长度不如链表方便。
从逻辑结构来看
1.数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;数组可以根据下标直接存取。
2. 链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项,非常繁琐)链表必须根据next指针找到下一个元素
从内存存储来看
1.(静态)数组从栈中分配空间, 对于程序员方便快速,但是自由度小
2. 链表从堆中分配空间, 自由度大但是申请管理比较麻烦
从上面的比较可以看出,如果需要快速访问数据,很少或不插入和删除元素,就应该用数组;相反, 如果需要经常插入和删除元素就需要用链表数据结构了。
3、链表静态添加和遍历
#include <stdio.h>
#include <stdlib.h>
struct Test
{
int data;
struct Test *next;
};
void printLink(struct Test *head)//链表遍历
{
struct Test *point;
point = head;
while(point != NULL){
printf("%d", point->data);
point = point->next;
}
putchar('\n');
}
int main()
{
struct Test t1 = {1, NULL};
struct Test t2 = {2, NULL};
struct Test t3 = {3, NULL};
t1.next = &t2;
t2.next = &t3;
printLink(&t1);//链表遍历
return 0;
}
4、统计链表个数及链表查找
#include <stdio.h>
#include <stdlib.h>
struct Test
{
int data;
struct Test *next;
};
void printLink(struct Test *head)//链表遍历
{
struct Test *point;
point = head;
while(point != NULL){
printf("%d", point->data);
point = point->next;
}
putchar('\n');
}
int totalNum(struct Test *head)//统计链表个数
{
int cnt=0;
struct Test *p = head;
while(p != NULL){
cnt++;
p=p->next;
}
return cnt;
}
int searchLink(struct Test *head,int data)//链表的查找
{
struct Test *p=head;
while(p != NULL){
if(p->data == data){
return 1;
}
p=p->next;
}
return 0;
}
int main()
{
int ret;
struct Test t1 = {1, NULL};
struct Test t2 = {2, NULL};
struct Test t3 = {3, NULL};
t1.next = &t2;
t2.next = &t3;
printLink(&t1);
ret = totalNum(&t1);
printf("total is %d\n",ret);
ret = searchLink(&t1,2);
if(ret == 0){
printf("no 2\n");
}else{
printf("has 2\n");
}
return 0;
}
5、 链表从指定节点后方插入新节点
链表 1-- 2-- 3 – 4—5
在3结点后插入新的100
1)找到3
2)new->next =3 ->next;
3) 3->next =new;
int insertBehind(struct Test *head, int data, struct Test *new)
{
struct Test *p = head;
while(p != NULL){
if(p->data == data){
new->next = p->next;
p->next = new;
return 1;
}
p = p->next;
}
return 0;
}
void printLink(struct Test *head)
{
struct Test *point;
point = head;
while(point != NULL){
printf(" %d ", point->data);
point = point->next;
}
putchar('\n');
}
int main()
{
int ret;
struct Test t1 = {1, NULL};
struct Test t2 = {2, NULL};
struct Test t3 = {3, NULL};
struct Test new = {100, NULL};
t1.next = &t2;
t2.next = &t3;
ret = insertBehind(&t1,3,&new);
printLink(&t1);
return 0;
}
//运行结果: 1 2 3 100
6、链表从指定节点前方插入新节点
链表 1-- 2-- 3
在1结点前插入新的101
1)在第一个结点前插入101,new->next = 1,此时链表的头发生了变化
在3结点前插入新的结点102
1)找到2->next->data =3
2)new->next =2->next;
3) 2->next =new;
struct Test *insertFor(struct Test *head, int data, struct Test *new)//因为会改头部,返回链表指针
{
struct Test *p = head;
if(p->data == data){ //在链表头部插入新节点
new->next = p;
return new;
}
while(p->next != NULL){ //插入的新节点不是头部
if(p->next->data == data){
new->next=p->next;
p->next=new;
return head;
}
p=p->next;
}
return head;
};
int main()
{
int ret;
struct Test *head = NULL;
struct Test t1 = {1, NULL};
struct Test t2 = {2, NULL};
struct Test t3 = {3, NULL};
struct Test t5 = {100, NULL};
t1.next = &t2;
t2.next = &t3;
head = &t1;
ret = insertBehind(head,3,&t5);
printLink(head);
struct Test new={101, NULL};
head = insertFor(head, 1, &new);
printLink(head);
struct Test new2={102, NULL};
head = insertFor(head, 3, &new2);
printLink(head);
return 0;
}
//运行结果:
// 1 2 3 100
//101 1 2 3 100
//101 1 2 102 3 100
7、链表删除指定节点
链表 1-- 2-- 3
1)删除第一个结点,改头
2)要删除的不是头,找到要删除的2, (1) 1->next->data=2, (2) 1->next=1->next->next
struct Test *deleteNum(struct Test *head, int data)
{
struct Test *p =head;
if(p->data == data){
head = p->next;
return head;
}
while(p != NULL){
if(p->next->data == data){
p->next=p->next->next;
return head;
}
p=p->next;
}
return head;
}
//运行结果: 1 3
8、链表动态创建之头插法
void printLink(struct Test *head)
{
struct Test *point;
point = head;
while(point != NULL){
printf(" %d ", point->data);
point = point->next;
}
putchar('\n');
}
struct Test *insertHead(struct Test *head)
{
struct Test *new;
while(1){
new = (struct Test *)malloc(sizeof(struct Test));//每次开辟空间
printf("input your num\n");
scanf("%d",&(new->data));
if(new->data ==0){ //输入0退出
printf("0 quit\n");
return head;
}
if(head == NULL){ //链表为空时,插入
head=new;
}else{
new->next=head; //头插法,新的下一个为头,插完后,把新的名为头
head=new;
}
}
return head;
};
int main()
{
struct Test *head = NULL;
head = insertHead(head); //返回值为头
printLink(head);
return 0;
}
9、链表动态创建之尾插法
void printLink(struct Test *head)
{
struct Test *point;
point = head;
while(point != NULL){
printf(" %d ", point->data);
point = point->next;
}
putchar('\n');
}
struct Test *insertBehind(struct Test *head, struct Test *new)
{
struct Test *p=head;
if(p == NULL){
head = new;
return head;
}
while(p->next != NULL){
p = p->next;
}
p->next = new;
return head;
}
struct Test *creatLink(struct Test *head)
{
struct Test *new;
while(1){
new = (struct Test*)malloc(sizeof(struct Test));
printf("input your nu\n");
scanf("%d", &(new->data));
if(new->data == 0){
printf("0 quit\n");
return head;
}
head = insertBehind(head,new);
}
}
int main()
{
struct Test *head = NULL;
head = creatLink(head);
printLink(head);
return 0;
}