Day 62 数据结构(单向链表,单向循环链表,双向链表)

1. 单向链表的设计

例程:创建一个动态单向链表 1.定义链表结点:数据域+指针域

2.定义链表结构体:头结点指针+结点数

3.初始化链表

4.指定位置插入新数据

5.删除指定位置数据

6.获取链表长度

7.依据数据查找所在链表位置

8.返回第一个结点

9.打印链表结点数据

10.释放链表

两个.c文件,一个头文件

linklist.c:

#include"linklist.h"

/* 初始化 */
LinkList *init_linklist(void)
{
    LinkList *list = (LinkList *)malloc(sizeof(LinkList));
    list->size = 0;

    //头结点
    list->head = (LinkList *)malloc(sizeof(LinkList));
    list->head->data = NULL;
    list->head->next=NULL;
    return list;
}

//指定位置插入,z
void insert_linklist(LinkList *list, int pos, void *data)
{
    int count;
    if(list == NULL)
    {
        return ;
    }
    if(data == NULL)
    {
        return ;
    }
    if(pos<0 || pos >list->size)
    {
        pos=list->size;
    }
    //创建新的结点
    LinkNode *newnode=(LinkNode *)malloc(sizeof(LinkNode));
    newnode->data=data;
    newnode->next=NULL;
    //找结点
    //辅助指针变量
    LinkNode *pcurrent=list->head;
    int i;
    for(i=0;i<pos;i++)
    {
        pcurrent=pcurrent->next;
    }
#if 0
    while(pcurrent!=NULL&&pcurrent->next!=NULL)
    {
        pcurrent=pcurrent->next;
        count++;
        if(count=pos)
    {
        break;
    }
    }
#endif
    //新结点插入链表
    newnode->next=pcurrent->next;
    pcurrent->next=newnode;
    list->size++;
    }
//删除指定位置的值
void del_by_pos_linklist(LinkList *list, int pos)
{
    if(list == NULL)
    {
        return ;
    }    
    if(pos<0 || pos >list->size)
    {
        return ;
    }
    //找结点
    //辅助指针变量
    LinkNode *pcurrent=list->head;
    int i;
    for(i=0;i<pos;i++)
    {
        pcurrent=pcurrent->next;
    }
    //删除结点
    LinkNode *delnode=pcurrent->next;
    pcurrent->next=delnode->next;
    free(delnode);
    list->size--;
    }
    //获取链表长度
    int size_linklist(LinkList *list)
    {
        return list->size;
    }
//查找
int find_linklist(LinkList *list, void *data)
{
    if(list == NULL)
    {
        return -1;
    }
    if(data == NULL)
    {
        return -2;
    }
    //遍历查找
    LinkNode *pcurrent = list->head->next;
    int i=1;
    while(pcurrent!=NULL)
    {
        if(pcurrent->data==data)
    {
    break;
    }
    i++;
    pcurrent=pcurrent->next;
    }
    return i;
}
//返回第一个结点
void *first_linklist(LinkList *list)
{
    return list->head->next->data;
}
//打印链表结点
void print_linklist(LinkList *list, printlinknode print)
{
    if(list == NULL)
    {
        return ;
    }
    //辅助指针变量
    LinkNode *pcurrent=list->head->next;
    while(pcurrent!=NULL)
    {
        print(pcurrent->data);
        pcurrent=pcurrent->next;
    }
}
//释放链表内存
void free_linklist(LinkList *list)
{
    if(list == NULL)
    {
        return ;
    }
    //辅助指针变量
    LinkNode *pcurrent=list->head;
    while(pcurrent!=NULL)
    {
        //缓存下一个结点
        LinkNode *pnext=pcurrent->next;
        free(pcurrent);
        pcurrent=pnext;
    }
    //释放链表内存
    list->size=0;
    free(list);
}
 

linklist.h

#ifndef LINKLIST_H
#define LINKLIST_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//链表节点
typedef struct NODE
{
    void *data;
    struct NODE *next;
}LinkNode;
//链表结构体
typedef struct
{
    LinkNode *head;
    int size;
}LinkList;
//打印函数指针
typedef void (*printlinknode)(void *);
//初始链表
LinkList *init_linklist(void);
//指定位置插入
void insert_linklist(LinkList *list, int pos, void *data);
//删除指定位置的值
void del_by_pos_linklist(LinkList *list, int pos);
//获取链表长度
int size_linklist(LinkList *list);
//依据数据查找所在位置
int find_linklist(LinkList *list, void *data);
//返回第一个结点
void *first_linklist(LinkList *list);
//打印链表结点
void print_linklist(LinkList *list, printlinknode print);
//释放链表内存
void free_linklist(LinkList *list);
#endif

main.c

#include "linklist.h"
typedef struct
{
    char name[64];
    int age;
    int score;
}Person;
//打印函数
void my_print(void *data)
{
    Person *p=(Person *)data;
    printf("name:%s age:%d score:%d\n",p->name,p->age,p->score);
}

int main()
{
    //创建链表
    LinkList *list=init_linklist();
    //获取链表长度
    printf("list 长度:%d\n",size_linklist(list));
    //创建数据
    Person p1={"大明",18,96};
    Person p2={"小明",19,97};
    Person p3={"老明",20,98};
    //数据插入链表
    insert_linklist(list, 0, &p1);//0表示头插法
    insert_linklist(list, 0, &p2);
    //打印
    print_linklist(list, my_print);
    printf("\n");
    //指定位置1处,插入
    insert_linklist(list, 1, &p3);
    //打印
    print_linklist(list, my_print);
    printf("\n");
    //依据数据找所在位置
    int ret=find_linklist(list, &p3);
    printf("\"老明\"所在位置:%d\n",ret);
    //指定位置1处,删除
    del_by_pos_linklist(list, 1);
    //打印
    print_linklist(list, my_print);
    printf("\n");
    //获取链表长度
    printf("list 长度:%d\n",size_linklist(list));
    //返回第一个结点
    Person *p=(Person *)first_linklist(list);
    printf("第一个结点:name-%s,age-%d,score-%d\n",p->name,p->age,p->score);
    //销毁
    free_linklist(list);
    return 0;
}

2. 单向循环链表

2.1 什么是单向循环链表?

如果把单链表的最后一个节点的指针指向链表头部,而不是指向NULL,那么就构成了一个单向循环链 表,通俗讲就是把尾节点的下一跳指向头结点使其形成一个闭环。

2.2 为什么要使用单向循环链表?

在单向链表中,头指针是相当重要的,因为单向链表的操作都需要头指针,所以如果头指针丢失或者破 坏,那么整个链表都会遗失,并且浪费链表内存空间,因此我们引入了单向循环链表这种数据结构。

2.3 单向循环链表我们需要注意两个问题:

在链表中我们使用的是虚拟头结点,但是在循环链表中我们在遍历的时候就会遇到麻烦,因此在单向循 环链表中我们使用的是真实头结点。

循环单链表没有明显的结束条件,当我们不注意时,很容易陷入死循环。此时,我们可以设置一个标记 点作为循环的标记,当我们知道表长,我们也可以设置一个计数器来结束循环。

单向循环链表插入节点:中间插法

2.4 单向循环链表设计

创建一个动态单向循环链表:

.定义链表结点:数据域+指针域

2.定义链表结构体:头结点指针

3.初始化链表

4.指定位置插入新数据(头插法,中间插法)

5.删除指定位置数据

6.获取链表长度

7.依据数据查找所在链表位置

8.返回第一个结点

9.打印链表结点数据

10.释放链表

例:实现单向循环链表的创建,增删改查及打印的功能 

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

typedef struct node{
	int data;
	struct node *next;
}link_t;

link_t *link_init(void);
static void insert_behind(link_t *p, link_t *node);
void link_add_tail(link_t *p, int d);
void display(link_t *p);
void link_del(link_t *p, int d);
void link_update(link_t *p, int old, int new);
int link_find(link_t *p, int d);

int main()
{
	link_t *phead = link_init();
	
	for(int i = 0; i < 6; i++)
	{
		link_add_tail(phead, i);	
	} 
	display(phead);
	
	link_del(phead, 3);
	display(phead);
	
	link_update(phead, 2, 22);
	display(phead);
	
	int ret = link_find(phead, 4);
	printf("找到节点的位置是%d\n", ret);
	display(phead);
	

	return 0;
}
//在堆中申请空间并进行初始化
link_t *link_init(void)
{
	//申请空间
	link_t *p = (link_t *)malloc(sizeof(link_t));
	if(p == NULL)
	{
		perror("initError");
		return NULL;
	}
	//给该空间的成员赋值
	p->next = p;
	return p; 
	
	 
}


static void insert_behind(link_t *p, link_t *node)
{
	node->next = p->next;
	p->next = node;	
}

/**
	***********************************
	*@brief 插入 尾插
	*@param p : 头结点的地址
	*@param d : 需要插入的数据
	***********************************
	*/
void link_add_tail(link_t *p, int d)
{
	//保存头结点,以便后期使用 
	link_t *head = p;
	
	//创建一个新的结点
	link_t *node = (link_t *)malloc(sizeof(link_t));
	if(node == NULL)
	{
		perror("addCreateError");
		exit(1);	
	}
	node->data = d;
	
	while(p->next != head)
	{
		p = p->next;
	}
	
	//将新结点插入尾巴后面
	insert_behind(p, node); 	 
}
/**
	***********************************
	*@brief 删除
	*@param p : 头结点的地址
	*@param d : 需要删除的数据
	***********************************
*/
void link_del(link_t *p, int d)
{
	link_t *head = p;
	link_t *delnode = NULL;
	
	//遍历去寻找d
	while(p->next != head)
	{
		if(p->next->data == d)
		{
			delnode = p->next;
			p->next = delnode->next;
			free(delnode);
			delnode = NULL;
			continue;
		}
		p = p->next;	
	} 
}

/**
	***********************************
	*@brief 修改
	*@param p : 头结点的地址
	*@param d : 需要删除的数据
	***********************************
*/
void link_update(link_t *p, int old, int new)
{
	link_t *head = p;
	
	//找到old的值
	while(p->next != head)
	{
		if(p->next->data == old)
		{
			p->next->data = new;	
		}
		p = p->next;	
	}	
}

/**
	***********************************
	*@brief 查找一个节点
	*@param p : 头结点的地址
	*@param d : 需要插入的数据
	*@retval
	***********************************
*/ 
int link_find(link_t *p, int d)
{
	link_t *head = p;
	int count = 0;
	while(p->next != head)
	{
		if(p->next->data == d)
		{
			return count + 1;
		}
		count++;
		p = p->next;
	}
	return -1;
}

/**
	***********************************
	*@brief 遍历
	*@param p : 头结点的地址
	*@param d : 需要插入的数据
	*@retval
	***********************************
*/
void display(link_t *p)
{
	link_t *head = p;
	printf("遍历结果为:\n");
	while(p->next != head)
	{
		p = p->next;
		printf("%d->", p->data);
	}
	printf("\b\b\n");
}

3. 双链表

就是在单链表的的每个结点中,再设置一个指针域,也就是每个结点都有两个指针域,一个指向它的前 驱结点,一个指向它的后继结点。

虽然使用单链表能 100% 解决逻辑关系为 "一对一" 数据的存储问题,但在解决某些特殊问题时,单链表 并不是效率最优的存储结构。比如说,如果算法中需要大量地找某指定结点的前趋结点,使用单链表无 疑是灾难性的,因为单链表更适合 "从前往后" 找,而 "从后往前" 找并不是它的强项。

为了能够高效率解决类似的问题,本节来学习双向链表(简称双链表)。

从名字上理解双向链表,即链表是 "双向" 的。

1. 指针域:用于指向当前节点的直接前驱节点;

2. 数据域:用于存储数据元素。

3. 指针域:用于指向当前节点的直接后继节点;

例:双向链表的创建,销毁,增(头部添加)删改查及显示。

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

/*双向链表结构*/
typedef struct List
{
	int data;
	struct List *next;
	struct List *front;	
}NODE;

NODE *CreatList();
void insertNode(NODE *head, int data);
void DeleteNode(NODE *head, int data);
void updateNode(NODE *head, int oldval, int newval);
int FindNode(NODE *head, int val);
void displayList(NODE *head);
void DestoryList(NODE *head);

int main()
{
	NODE *head = CreatList();
	if(head == NULL)
	{
		perror("headCreateError");
		exit(1);
	}
	//给双向链表插入数据
	for(int i = 0; i <= 6; i++)
	{
		insertNode(head, i);
	}
	//显示链表数据
	displayList(head);
	
	DeleteNode(head, 4);
	displayList(head);
	
	updateNode(head, 2, 222);
	displayList(head);
	
	int ret = FindNode(head, 222);
	printf("要找的结点是:%d\n", ret);
	
	DestoryList(head);
	
	return 0;
}

/* 销毁链表 */
void DestoryList(NODE *head)
{
	NODE *tmp;
	while(head->next != NULL)
	{
		tmp = head;
		head = head->next;
		free(tmp);
		tmp = NULL;	
	}
	free(head);
	head = NULL;
	printf("空间已释放\n");	
} 

/* 双向链表的遍历 */
void displayList(NODE *head)
{
	printf("------------------\n");
	if(head == NULL)
	{
		perror("headEmptyError");
		exit(1);
	}
	NODE *tmpHead = head;
	while(tmpHead->next != NULL)
	{
		tmpHead = tmpHead->next;
		printf("%d->", tmpHead->data);	
	}
	
	//倒着遍历
	while(tmpHead->front->front != NULL)
	{
		printf("%d<-", tmpHead->data);
		tmpHead = tmpHead->front;	
	}
	printf("%d\n", tmpHead->data);	
} 

//寻找值 
int FindNode(NODE *head, int val)
{
	int count = 1;
	if(head == NULL)
	{
		perror("headEmptyError");
		return -1;
	}
	NODE *tmpHead = head;
	while(tmpHead->next != NULL)
	{
		tmpHead = tmpHead->next;
		if(tmpHead->data == val)
		{
			return count;	
		}
		count++; 
	}
	return -1;
}

void updateNode(NODE *head, int oldval, int newval)
{
	if(head == NULL)
	{
		perror("headEmptyError");
		exit(1);
	}
	NODE *tmpHead = head;
	while(tmpHead->next != NULL)
	{
		tmpHead = tmpHead->next;
		if(tmpHead->data == oldval)
		{
			tmpHead->data = newval;
			continue; 
		}
	}
}

/* 删除一个结点 */
void DeleteNode(NODE *head, int data)
{
	if(head == NULL)
	{
		perror("headEmpty");
		exit(1);	
	}
	NODE *tmpHead = head;
	while(tmpHead->next != NULL)
	{
		tmpHead = tmpHead->next;
		if(tmpHead->data == data)
		{
			tmpHead->front->next = tmpHead->next;
			tmpHead->next->front = tmpHead->front;
			break;	
		}	
	}
	free(tmpHead);
	tmpHead = NULL;	
}

/* 双向链表插入节点,头插法 */
void insertNode(NODE *head, int data)
{
	if(head == NULL)
	{
		perror("headNullError");
		exit(1); 
	}
	NODE *temHead = head;//创建一个临时结点,指向头结点
	if(temHead->next == NULL)
	{
		/* 当双向链表只有一个头结点 */
		NODE *addition = (NODE *)malloc(sizeof(NODE));
		if(addition == NULL)
		{
			perror("additionError");
			exit(1);	
		}
		addition->data = data;
		addition->next = temHead->next;
		temHead->next = addition;
		addition->front = temHead;	
	}
	else
	{
		/* 当双向链表除了头结点还有其他数据时 */
		NODE *addition = (NODE *)malloc(sizeof(NODE));
		if(addition == NULL)
		{
			perror("additionCreateError");
			exit(1);	
		}
		addition->data = data;
		temHead->next->front = addition;
		addition->front = temHead;
		addition->next = temHead->next;
		temHead->next = addition;	
	} 
}

NODE *CreatList()
{
	NODE *head = (NODE *)malloc(sizeof(NODE));
	if(head == NULL)
	{
		perror("CreateNodeError");
		return NULL;	
	}
	head->next = head->front = NULL;
	//链表初始化
	return head;	
}
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值