嵌入式学习-线性表Day02-链表

链表

特点:内存不连续,通过指针进行连接

解决:长度固定问题,插入、删除麻烦的问题

1)逻辑结构 : 线性结构

2)存储结构 :链式存储

3)操作 : 增删改查

单向链表

结构体:

struct node_t

{

int data;//数据域

struct node_t * next;//指针域,存放下一个节点地址

};

分类

1)有头单向链表

存在一个头节点,数据域无效,指针域有效

遍历有头单向链表:

#include<stdio.h>
typedef struct node_t
{
    int data;
    struct node_t *next;
}link_node_t,* link_list_t;
//link_list_t == link_node_t *
int main(int argc, char const *argv[])
{
    //1.定义3个节点
    link_node_t a = {1,NULL};
    link_node_t b = {2,NULL};
    link_node_t c = {3,NULL};
    //2.将节点进行连接
    a.next = &b;
    b.next = &c;
    //3.定义一个头节点指向第一个节点
    link_node_t s ;
    s.next = &a;
    //4.定义一个头指针指向头结点
    // link_node_t * h = &s;
    
    link_list_t h = &s;
    //5.遍历有头单向链表
    //方法1:直接遍历有头单向链表
    /*
    while(h->next != NULL)
    {
        h=h->next;
        printf("%d ",h->data);
    }
    
    */
   //方法2:先变成无头,再遍历
   h=h->next;
   while (h!= NULL)
   {
     printf("%d ",h->data);
     h=h->next;
   }
   printf("\n");

    return 0;
}


2)无头单向链表

所有节点的数据域、指针域都有效

遍历无头单向链表:

#include<stdio.h>
typedef struct node_t
{
    int data;
    struct node_t *next;
}link_node_t,* link_list_t;
//link_list_t == link_node_t *
int main(int argc, char const *argv[])
{
    //1.定义3个节点
    link_node_t a = {1,NULL};
    link_node_t b = {2,NULL};
    link_node_t c = {3,NULL};
    //2.将节点进行连接
    a.next = &b;
    b.next = &c;

    //3.定义一个头指针指向第一个结点
    // link_node_t * h = &a;
    
    link_list_t h = &a;

    //4.遍历无头单向链表
    while(h != NULL)
    {
        printf("%d ",h->data);
        h=h->next; 
    }
    printf("\n");

    return 0;
}


操作函数

linklist.h

#ifndef _LINKLIST_H_
#define _LINKLIST_H_

#include<stdio.h>
#include<stdlib.h>

typedef int datatype;
typedef struct node_t
{
	datatype data;//数据域
	struct node_t *next;//指针域,指向自身结构体的指针
}link_node_t,*link_list_t;

//1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList();
//2.向单向链表的指定位置插入数据
//p保存链表的头指针 post 插入的位置 data插入的数据
int InsertIntoPostLinkList(link_node_t *p,int post, datatype data);
//3.遍历单向链表
void ShowLinkList(link_node_t *p);
//4.求单向链表长度的函数
int LengthLinkList(link_node_t *p);
//5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int DeletePostLinkList(link_node_t *p, int post);
//6.判断单向链表是否为空 1代表空 0代表非空
int IsEpLinkList(link_node_t *p);
//7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data);
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data);
//9.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data);
//10.转置链表
void ReverseLinkList(link_node_t *p);
//11.清空单向链表
void ClearLinkList(link_node_t *p);
#endif



1)创建一个空的(有头)单向链表

2)向post位置插入一个数据

  1. 先遍历找到要插入节点的前一个节点,假设这个节点为A;A的下一个节点为B;将C插入A与B之间;
  2. 先让C的指针域指向B
  3. 再让A的指针域指向C

注意:顺序不可以调换

3)删除指定位置的数据

4)单向链表的转置

转置的思想

(1) 将头节点与当前链表断开,断开前保存下头节点的下一个节点,保证后面链表能找得到,定义一个q保存头节点的下一个节点,断开后前面相当于一个空的链表,后面是一个无头的单向链表

(2) 遍历无头链表的所有节点,将每一个节点当做新节点插入空链表头节点的下一个节点(每次插入的头节点的下一个节点位置)

linklist.c

#include "linklist.h"

// 1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList()
{
    // 1.开辟空间用来存放头结点
    link_list_t p = (link_list_t)malloc(sizeof(link_node_t));
    if (p == NULL)
    {
        perror("CreateEpLinkList ERR");
        return NULL;
    }
    // 2.初始化
    p->next = NULL;
    return p;
}
// 4.求单向链表长度的函数
int LengthLinkList(link_node_t *p)
{
    int num = 0;
    while (p->next != NULL)
    {
        p = p->next;
        num++;
    }
    return num;
}
// 2.向单向链表的指定位置插入数据
// p保存链表的头指针 post 插入的位置 data插入的数据
int InsertIntoPostLinkList(link_node_t *p, int post, datatype data)
{
    // 0.容错判断
    if (post < 0 || post > LengthLinkList(p))
    {
        perror("InsertIntoPostLinkList err");
        return -1;
    }
    // 1.将头指针指向被插入位置的前一个节点
    for (int i = 0; i < post; i++)
        p = p->next;
    // 2.创建一个新节点,存放数据
    link_list_t pnew = (link_list_t)malloc(sizeof(link_node_t));
    if (pnew == NULL)
    {
        perror("pnew malloc err");
        return -1;
    }
    pnew->data = data;
    pnew->next = NULL;
    // 3.将新节点插入链表中(先连后面,再连前面)
    pnew->next = p->next;
    p->next = pnew;
    return 0;
}
//6.判断单向链表是否为空 1代表空 0代表非空
int IsEpLinkList(link_node_t *p)
{
    return p->next == NULL;
}
// 5.删除单向链表中指定位置的数据 post 代表的是删除的位置
int DeletePostLinkList(link_node_t *p, int post)
{
    // 0.容错判断
    if (post < 0 || post >= LengthLinkList(p))
    {
        perror("DeletePostLinkList err");
        return -1;
    }
    // 1.将头指针指向被删除位置的前一个节点
    for (int i = 0; i < post; i++)
        p = p->next;
    // 2.进行删除操作
    // 1)定义一个指针pdel指向被删除位置
    link_list_t pdel = p->next;
    // 2)跨过被删除节点
    p->next = pdel->next;
    // 3)释放被删除节点
    free(pdel);
    pdel = NULL;
    return 0;
}
//3.遍历单向链表
void ShowLinkList(link_node_t *p)
{
    while (p->next !=NULL)
    {
        p=p->next;
        printf("%d ",p->data);
    }
    printf("\n");
    
}
//7.修改指定位置的数据 post 被修改的位置 data修改成的数据
int ChangePostLinkList(link_node_t *p, int post, datatype data)
{
    //0.容错判断
    if(post < 0 || post >=LengthLinkList(p))
    {
        perror("ChangePostLinkList");
        return -1;
    }
    //1.将头指针指向被修改的节点
    for(int i=0;i<=post;i++)
        p=p->next;
    //2.修改数据
    p->data = data;
    return 0;

}
//8.查找指定数据出现的位置 data被查找的数据 //search 查找
int SearchDataLinkList(link_node_t *p, datatype data)
{
    //1.定义变量记录查找位置
    int post=0;
    //2.遍历链表,查找数据
    while(p->next != NULL)
    {
        p=p->next;
        if(p->data == data)
            return post;
        post++;
    }
    return -1;
}
//9.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
int DeleteDataLinkList(link_node_t *p, datatype data)
{
    //1.定义一个指针q指向头结点的写一个节点,此时可以看做q指向一个无头单向链表
    link_list_t q = p->next;
    //2.用q遍历链表,将每一个节点的数据域与data作比较,如果相同就删除该节点
    while(q != NULL)
    {
        if(q->data == data)//如果相等,删除
        {
            p->next = q->next;
            free(q);
            q = p->next;
        }
        else//如果不相等,指针向后移动
        {
            p=p->next;
            q=p->next;
        }
    }
    return 0;
}
//11.清空单向链表
void ClearLinkList(link_node_t *p)
{
    while (!IsEpLinkList(p))
        DeletePostLinkList(p,0);
    
}
//10.转置链表
//解题思想:
//(1) 将头节点与当前链表断开,断开前保存下头节点的下一个节点,
//保证后面链表能找得到,定义一个q保存头节点的下一个节点,
//断开后前面相当于一个空的链表,后面是一个无头的单向链表

//(2) 遍历无头链表的所有节点,将每一个节点当做新节点
//插入空链表头节点的下一个节点(每次插入的头节点的下一个节点位置)
void ReverseLinkList(link_node_t *p)
{
    //1.断开前
    link_list_t q = p->next;
    link_list_t temp = NULL;
    //2.断开链表
    p->next = NULL;
    //3.遍历无头单向链表,依次插入头节点后面
    while ( q != NULL)
    {
        temp = q->next;
        q->next = p->next;
        p->next = q;
        q = temp;

    }
    
}

main.c

#include"linklist.h"
int main(int argc, char const *argv[])
{
    link_list_t p = CreateEpLinkList();

    for(int i=0;i<5;i++)
        InsertIntoPostLinkList(p,i,i+1);
    ShowLinkList(p);//1 2 3 4 5
    DeletePostLinkList(p,0);
    ShowLinkList(p);//2 3 4 5
    ReverseLinkList(p);
    ShowLinkList(p);//5 4 3 2 
    DeleteDataLinkList(p,3);
    ShowLinkList(p);//5 4 2
    printf("%d post is %d\n",5,SearchDataLinkList(p,5));//0
    while (!IsEpLinkList(p))
       DeletePostLinkList(p,0);
    free(p);
    p=NULL;
    return 0;
}

单向循环链表

解决约瑟夫问题

约瑟夫问题为:设编号为1,2,……n得n个人围坐一圈,约定编号为k(k大于等于1并且小于等于n)的人从1开始报数,数到m的那个人出列。它的下一位继续从1开始报数,数到m的人出列,依次类推,最后剩下一个为猴王。

直接上图展示,初始化状态: 假设n=6,总共有6个人,k=1,从第一个人开始报数,m=5,每次数五个。

  第一次报数:从一号开始,数五个数,1-2-3-4-5,数完五个数,五号被杀死,第一次报数后,剩余人数如下。

第二次报数: 从被杀死的五号的下一位开始报数,也就是六号,数五个数,6-1-2-3-4,数数完毕,四号被杀死,第二次报数后,剩余人数如下

第三次报数: 从被杀死的四号的下一位开始报数,同样是六号,数五个数,6-1-2-3-6,数数完毕,六号被杀死,第三次报数后,剩余人数如下。

第四次报数: 从被杀死的六号的下一位开始报数,也就是一号,数五个数,1-2-3-1-2,数数完毕,二号被杀死,第四次报数后,剩余人数如下。

第五次报数: 从被杀死的二号的下一位开始报数,也就是三号,数五个数,3-1-3-1-3,数数完毕,三号被杀死,只剩下一号

#include <stdio.h>
#include <stdlib.h>

typedef struct node_t
{
	int data;
	struct node_t *next;
}link_node_t,*link_list_t;

int main(int argc, const char *argv[])
{
	int i;
	link_list_t pdel = NULL;//用于指向被删除节点
	link_list_t ptail = NULL;//永远指向当前链表的尾 
	link_list_t pnew = NULL;//永远指向新创建的节点
	link_list_t h = NULL;
	int all_num = 7;//猴子总数 
	int start_num = 2; //从几号猴子开始数
	int kill_num = 3;//数到几杀死猴
	// printf("请您入猴子总数 起始号码 数到几杀死:\n");
	// scanf("%d%d%d",&all_num,&start_num,&kill_num);
	//1.创建出一个单向循环链表
	//(1)创建有all_num个节点的单向链表
	h = (link_list_t)malloc(sizeof(link_node_t));
	if(NULL == h)
	{
		perror("malloc failed");
		return -1;
	}
	h->data = 1;
	h->next = NULL;
	ptail = h;//尾指针指向当前的第一个节点
	for(i = 2; i <= all_num; i++)
	{
		//创建新的节点
		pnew = (link_list_t)malloc(sizeof(link_node_t));
		if(NULL == pnew)
		{
			perror("malloc failed");
			return -1;
		}
		//将新节点装上数据
		pnew->data = i;
		pnew->next = NULL;
		//将新节点链接到链表尾 
		ptail->next = pnew;//链接到链表的尾
		ptail = pnew;//尾指针继续指向当前链表的尾 
	}
	//(2)将头指针保存到链表的尾形成单向循环链表
	ptail->next = h;//形成单向循环链表 
#if 0 //用于调试程序
	while(1)
	{
		printf("%d\n",h->data);
		h = h->next;
		sleep(1);
	}
#endif
	//2.开始杀猴子 
	//(1)将头指针移动到开始猴子的号码处 
	for(i = 0; i < start_num-1; i++)
		h = h->next;
	//(2)循环进行杀猴子
	while(h != h->next)//条件不成的时候,就剩一个猴子,只有一个节点
	{
		//将头指针移动到即将删除节点的前一个节点
		for(i = 0; i < kill_num-2; i++)
			h = h->next;

		pdel = h->next;
		//跨过删除节点
		h->next = pdel->next;
		printf("kill is -------------%d\n",pdel->data);
		free(pdel);
		pdel = NULL;
		//杀死猴子猴,从下一个节点开始继续开始数,将头指针移动到开始数的地方
		h = h->next;
	}
	printf("king is=================== %d\n",h->data);
	return 0;
}	

总结:

顺序表和链表的区别

(1)顺序表在内存当中连续存储的(数组),但是链表在内存当中是不连续存储的,通过指针将数据链接在一起

  1. 顺序表的长度是固定的,但是链表长度不固定
  2. 顺序表查找方便,但是插入和删除麻烦,链表,插入和删除方便,查找麻烦
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值