【数据结构】链表

前言:

我比较喜欢用“结点”而不是“节点”


定义链表的结点结构体:

//单链表

struct node
{
    int num;
    struct node * next;
};

typedef struct node Node;
typedef Node * Link;
//双链表

struct dbnode
{
    int num;
    struct node * next;
    struct node * prior;
};

typedef struct dbnode dbNode;
typedef Node * dbLink;

链表主要有以下这么几种操作:

void init(Link *head); //初始化链表
void display(Link head); //输出链表
void release(Link *head); //释放链表
void destroy(Link head); //清空链表
void create_node(Link *new_node); //结点分配空间
int is_malloc_ok(Link new_node); //结点分配成功

void insert_head(Link head,Link new_node); //头插入
void insert_tail(Link head,Link new_node); //尾插入
void insert_mid_head(Link head, Link new_node, int num); //在num前插入(没有num则不插入)
void insert_mid_tail(Link head,Link new_node,int num); //在num后插入(没有num则不插入)
void delete(Link head,int num); //删除

单链表、双链表、循环链表、等等

这些都是线性表的链式存储结构,我们在做题目做项目的时候,可以去考虑使用哪一种来解决问题,具体问题具体分析

/********************************
   给结点分配空间
   当分配不成功的时候重新分配
********************************
void create_node(Link *new_node)
{
    do{

        *new_node = (Link) malloc (sizeof(dblNode));

    }while(*new_node == NULL);

}

/********************************
   判断结点是否分配成功
 
   给结点分配空间
   当分配不成功的时候重新分配
*********************************/
#define MALLOC_MAX 10

int is_malloc_ok(dbLink new_node)
{
    if(NULL == new_node)
    {
        return 0;
    }
    return 1;
}


void create_node(Link *new_node)
{
    int count = MALLOC_MAX;
 
    do
    {
        *new_node = (Link)malloc(sizeof(Node));

        count--;

    }while((!is_malloc_ok(*new_node)) && (count != 0));
}
/********************************
   随机给结点赋值
*********************************/
#include <stdlib.h>
#include <time.h>

void random_link(dbLink head,int n)
{
    int i;

    dbLink new_node;

    for(i = 0;i < n;i++)
    {
        create_node(&new_node);

        new_node->num = rand() % 100;

        //insert_head(head,new_node);
        insert_tail(head,new_node);

    }
}

 

单向链表

单向链表仅包含两个域(数据域和指针域)

数据域存储结点的数据

指针域指向表中的下一个结点,而最后一个节点则指向NULL

适用于简单操作,或者增加和删除操作比较多的具体应用中

单向链表只可向一个方向遍历。查找一个结点的时候需要从从第一个结点依次遍历,直到访问到需要的位置。(不一定似的从第一个结点,看程序员自己的想法)

带头结点的单链表比较常用

 

双向链表
双向链表包含(一个数据域和两个指针域)

头结点的prior指向NULL,尾结点的next指向NULL。

这样可以从任何一个结点访问其前驱结点、后继结点,以至整个链表。

适用于需要大批量数据查询的具体应用中

由于占用额外的空间以存储指针,若是用来实现简单操作,可能会浪费空间,因为单链表即可解决这些问题。

循环的双向链表比较常用

//带头结点的双链表

/***********************************************
   头插
************************************************/
void insert_head(dbLink head,dbLink new_node)
{
    new_node->next = head->next;

    new_node->prior = head;

    head->next = new_node;

    if(new_node->next != NULL)
	{
    
	    new_node->next->prior = new_node;
    }

}

/***********************************************
   尾插
************************************************/
void insert_tail(dbLink head,dbLink new_node)
{
	dbLink p = head;

	while(p->next != NULL)
	{
		p = p->next;
	}

	new_node->next = p->next;

	new_node->prior = p;

	p->next = new_node;
	
}

/***********************************************
   删除
************************************************/
void delete(dbLink head,int num )
{
    dbLink p = head->next;

    while(p != NULL && p->num != num)
    {
        p = p->next;
    }

    if(p != NULL)
    {
        p->prior->next = p->next ;

		if(p->next != NULL)
		{
        	p->next->prior = p->prior;
		}

        free(p);
    }
    else
    {
        printf("找不到该结点,无法执行删除!\n");
    }

}


循环链表

循环链表的结构体与单链表、双链表相同

唯一的不同点是:单链表的尾结点next指针域指向链表头;双链表的头结点prior指向尾结点,尾结点的next指向头

使用带头结点的循环单链表实现插入和删除操作时,算法实现较为方便

只要知道表中任何一个结点的地址,就可以遍历链表中任意结点

适用于需要进行大量增加、删除元素操作而对访问元素无要求的,及预先无法确定表的大小的具体问题

//循环双链表

/***********************************************
   头插
************************************************/
void insert_head(dbLink head,dbLink new_node)
{
    new_node->next = head->next;

    new_node->prior = head;

    head->next = new_node;

	new_node->next->prior = new_node;


}

/***********************************************
   尾插
************************************************/
void insert_tail(dbLink head,dbLink new_node)
{
    //head->prior = new_node;

    new_node->prior = head->prior;
    
    //head->next = new_node;
    head->prior->next = new_node;

    head->prior = new_node;

    new_node->next = head;

}

/***********************************************
   删除
************************************************/

 

相关题目:(没来得及写)

  1. 题目:利用链表实现一个先入后出的栈结构,并提供栈操作的push和pop的接口
  2. 题目:使用双向链表来实现双向队列,并且让队列具有以下的操作:(1)判断队列是否为空(2)得到双向队列中元素的个数(3)向左端添加一个新元素(4)向右端添加一个新元素(5)从左端删除一个元素(6)从右端删除一个元素
  3. 排序链表(按大小插入)
  4. 排序链表(插入后排序)
  5. 反转单向和双向链表
  6. 反转部分单向链表
  7. 环形单链表的约瑟夫问题
  8. 程序功能:建立一个带有头结点的单向链表,并将存储在数组中的字符依次转储到链表的各个结点中。
  9. 编写程序STUDENT *Create(STUDENT studs[],int n)。STUDENT是一个结构类型,包含姓名、成绩和指针域。studs数组中存储了n个STUDENT记录。create函数的功能是根据studs数组建立一个链表,链表中结点按成绩降序排列,函数返回链表头指针。
  10. 打印两个有序链表的公共部分
  11. 在单链表和双链表中删除倒数第K个结点
  12. 删除链表的中间结点和a/b处的结点
  13. 判断一个链表是否为回文结构
  14. 将单向链表按某值划分成左边小、中间相等、右边大的形式
  15. 赋值含有随机指针结点的链表
  16. 两个单链表生成相加链表
  17. 两个单链表相交的一系列问题
  18. 将单链表的每K结点之间逆序
  19. 删除无序单链表中值重复出现的结点
  20. 将搜索二叉树转换成双向链表
  21. 单链表的选择排序
  22. 一种怪异的结点删除方式
  23. 向有序的环形单链表中插入新结点
  24. 合并两个有序的单链表
  25. 按照左右半区的方式重新组合单链表

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值