zoukankan      html  css  js  c++  java
  • 数据结构与算法-线性表

    线性表作为一种常用的数据结构,在实际中有着广泛的运用。

    线性表基本概念

    线性表定义

    • 线性表(List)是零个或多个数据元素的集合
    • 线性表中的数据元素之间是有顺序的
    • 线性表中的数据元素个数是有限的
    • 线性表中的数据元素的类型必须相同

    数学定义

    线性表是具有相同类型的 n( ≥ 0)个数据元素的有限序列
    (a1, a2,···, an)
    ai是表项,n 是表长度

    性质

    a0为线性表的第一个元素,只有一个后继
    an为线性表的最后一个元素,只有一个前驱
    除a0和an外的其它元素ai,既有前驱,又有后继
    线性表能够逐项访问和顺序存取

    线性表的操作

    创建线性表
    销毁线性表
    清空线性表
    将元素插入线性表
    将元素从线性表中删除
    获取线性表中某个位置的元素
    获取线性表的长度

    线性表在程序中表现为一种特殊的数据类型
    线性表的操作在程序中的表现为一组函数

    #ifndef __LIST_H_
    #define __LIST_H_
    
    typedef void List;
    typedef void ListNode;
    
    //创建并且返回一个空的线性表
    List* LinkList_Create();
    
    //销毁一个线性表list
    void List_Destroy(List* list);
    
    //将一个线性表list中的所有元素清空, 线性表回到创建时的初始状态
    void List_Clear(List* list);
    
    //返回一个线性表list中的所有元素个数
    int List_Length(List* list);
    
    //向一个线性表list的pos位置处插入新元素node
    int List_Insert(List* list, ListNode* node, int pos);
    
    //获取一个线性表list的pos位置处的元素
    ListNode* List_Get(List* list, int pos);
    
    //删除一个线性表list的pos位置处的元素  返回值为被删除的元素,NULL表示删除失败
    ListNode* List_Delete(List* list, int pos);
    
    #endif
    

    线性表的顺序存储结构

    基本概念

    顺序存储

    设计与实现

    • 插入元素算法
      判断线性表是否合法
      判断插入位置是否合法
      把最后一个元素到插入位置的元素后移一个位置
      将新元素插入
      线性表长度加1

    • 获取元素操作
      判断线性表是否合法
      判断位置是否合法
      直接通过数组下标的方式获取元素

    • 删除元素算法
      判断线性表是否合法
      判断删除位置是否合法
      将元素取出
      将删除位置后的元素分别向前移动一个位置
      线性表长度减1

    (*.h)

    #ifndef __MY_SEQLIST_H__ 
    #define __MY_SEQLIST_H__
    
    typedef void SeqList;
    typedef void SeqListNode;
    
    SeqList* SeqList_Create(int capacity);
    
    void SeqList_Destroy(SeqList* list);
    
    void SeqList_Clear(SeqList* list);
    
    int SeqList_Length(SeqList* list);
    
    int SeqList_Capacity(SeqList* list);
    
    int SeqList_Insert(SeqList* list, SeqListNode* node, int pos);
    
    SeqListNode* SeqList_Get(SeqList* list, int pos);
    
    SeqListNode* SeqList_Delete(SeqList* list, int pos);
    
    #endif
    

    (*.c)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "seqlist.h"
    
    typedef struct _tag_SeqList
    {
    	int capacity;
    	int length;
    	unsigned int *node; //unsigned int array[capacity]
    }TSeqList;
    
    SeqList* SeqList_Create(int capacity)
    {
    	TSeqList *ret = NULL;
    	if (capacity < 0)
    	{
    		return NULL;
    	}
    	ret = (TSeqList *)malloc(sizeof(TSeqList) + sizeof(unsigned int)*capacity);
    	if (ret == NULL)
    	{
    		return NULL;
    	}
    	memset(ret, 0, sizeof(TSeqList) + sizeof(unsigned int)*capacity);
    	ret->node = (unsigned int *)(ret + 1); //ret向后跳sizeof(TSeqList)
    	ret->capacity = capacity;
    	ret->length = 0;
    	return ret;
    }
    
    void SeqList_Destroy(SeqList* list)
    {
    	if (list == NULL)
    	{
    		return;
    	}
    	free(list);
    	return;
    }
    
    //链表清零
    void SeqList_Clear(SeqList* list)
    {
    	TSeqList *tList = NULL;
    
    	if (list == NULL)
    	{
    		return;
    	}
    	tList = (TSeqList *)list;
    	tList->length = 0;
    	return;
    }
    
    int SeqList_Length(SeqList* list)
    {
    	TSeqList *tList = NULL;
    	tList = (TSeqList *)list;
    	if (list == NULL)
    	{
    		return -1;
    	}
    
    	return tList->length;
    }
    
    //线性表的容量和线性表长度是不一样的
    int SeqList_Capacity(SeqList* list)
    {
    	TSeqList *tList = NULL;
    	tList = (TSeqList *)list;
    	if (list == NULL)
    	{
    		return -1;
    	}
    
    	return tList->capacity;
    }
    
    int SeqList_Insert(SeqList* list, SeqListNode* node, int pos)
    {
    	int			i = 0;
    	TSeqList	*tList = NULL;
    
    	tList = (TSeqList *)list;
    
    	if (list == NULL || node == NULL)
    	{
    		return -1;
    	}
    
    	//查看是不是满了
    	if (tList->length >= tList->capacity)
    	{
    		return -2;
    	}
    
    	//位置错误判断
    	if (pos < 0 || pos >= tList->capacity)
    	{
    		return -3;
    	}
    
    	//优化的容错。。。
    	if (pos >= tList->length)
    	{
    		pos = tList->length;
    	}
    
    	//插入算法
    	//从pos位置处开始,把数组后面的元素依此后移
    	for (i = tList->length; i > pos; i--)
    	{
    		//把前的元素往后移
    		tList->node[i] = tList->node[i - 1];
    	}
    	//循环跳出以后,pos正好是,要出入的位置
    	tList->node[pos] = (unsigned int)node;
    	tList->length++;
    	return 0;
    }
    
    SeqListNode* SeqList_Get(SeqList* list, int pos)
    {
    
    	SeqListNode		*ret = NULL;
    	TSeqList		*tList = NULL;
    
    	tList = (TSeqList *)list;
    	if (list == NULL || pos < 0 || pos >= tList->length)
    	{
    		return NULL;
    	}
    	ret = (SeqListNode*)tList->node[pos];
    	return ret;
    }
    
    SeqListNode* SeqList_Delete(SeqList* list, int pos)
    {
    	int				i;
    	TSeqList		*tList = NULL;
    	SeqListNode		*ret = NULL;
    
    	tList = (TSeqList *)list;
    
    	if (list == NULL || pos < 0 || pos >= tList->length)
    	{
    		return NULL;
    	}
    
    	//赋给之前,要把元素缓存下来
    	ret = (SeqListNode *)tList->node[pos];
    	//删除算法
    	for (i = pos + 1; i < tList->length; i++)
    	{
    		tList->node[i - 1] = tList->node[i];
    	}
    	tList->length--;
    
    	return ret;
    }
    

    (test)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "seqlist.h"
    
    typedef struct _Teacher
    {
    	char name[64];
    	int age;
    }Teacher;
    
    int main()
    {
    	int i = 0, len = 0;
    	SeqList *list = NULL;
    
    	Teacher t1, t2, t3;
    	t1.age = 31;
    	t2.age = 32;
    	t3.age = 33;
    
    	list = SeqList_Create(10);
    
    	//头插法
    	SeqList_Insert(list, (SeqListNode*)&t1, 0);
    	SeqList_Insert(list, (SeqListNode*)&t2, 0);
    	SeqList_Insert(list, (SeqListNode*)&t3, 0);
    
    	//循环遍历
    	for (i = 0; i < SeqList_Length(list); i++)
    	{
    		Teacher *tmp = (Teacher *)SeqList_Get(list, i);
    		if (tmp != NULL)
    		{
    			printf("age:%d ", tmp->age);
    		}
    	}
    
    	//循环删除
    	len = SeqList_Length(list);
    	for (i = 0; i < len; i++)
    	{
    		SeqList_Delete(list, 0);
    	}
    	SeqList_Destroy(list);
    
    	system("pause");
    }
    

    优点和缺点

    • 优点:
      无需为线性表中的逻辑关系增加额外的空间
      可以快速的获取表中合法位置的元素

    • 缺点:
      插入和删除操作需要移动大量元素
      当线性表长度变化较大时难以确定存储空间的容量

    线性表的链式存储

    基本概念

    为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息
    顺序存储1
    顺序存储2

    • 表头结点
      链表中的第一个结点,包含指向第一个数据元素的指针以及链表自身的一些信息
    • 数据结点
      链表中代表数据元素的结点,包含指向下一个数据元素的指针和数据元素的信息
    • 尾结点
      链表中的最后一个数据结点,其下一元素指针为空,表示无后继

    设计与实现

    在C语言中可以用结构体来定义链表中的指针域
    链表中的表头结点也可以用结构体实现
    顺序存储定义

    • 插入元素
      顺序存储插入操作
      带头结点、位置从0的单链表
      返回链表中第3个位置处,元素的值
    LinkListNode* LinkList_Get(LinkList* list, int pos)
    { 
    	int  i = 0;
    	TLinkList *tList = (TLinkList *)list;
    	LinkListNode *current = NULL;
    	LinkListNode *ret = NULL;
    
    	if (list==NULL ||pos<0 || pos>=tList->length)
    	{
    		return NULL;
    	}
    	current = (LinkListNode *)tList;
    	for (i=0; i<pos; i++)
    	{
    		current = current->next;
    	}
    	ret = current->next;
    	return ret ;
    }
    

    返回第三个位置的
    移动pos次以后,当前指针指向哪里?
    答案:指向位置2,所以需要返回 ret = current->next;
    备注:循环遍历时,遍历第1次,指向位置0;遍历第2次,指向位置1;遍历第3次,指向位置2;遍历第n次,指向位置n-1;
    所以如果想返回位置n的元素的值,ret = current->next;

    • 删除元素
      顺序存储删除操作
      (*.h)
    #ifndef __MY_LINKLIST_H_
    #define __MY_LINKLIST_H_
    
    typedef void LinkList;
    /*
    typedef struct _tag_LinkListNode LinkListNode;
    struct _tag_LinkListNode
    {
    LinkListNode* next;
    };
    */
    typedef struct _tag_LinkListNode
    {
    	struct _tag_LinkListNode* next;
    }LinkListNode;
    
    LinkList* LinkList_Create();
    
    void LinkList_Destroy(LinkList* list);
    
    void LinkList_Clear(LinkList* list);
    
    int LinkList_Length(LinkList* list);
    
    int LinkList_Insert(LinkList* list, LinkListNode* node, int pos);
    
    LinkListNode* LinkList_Get(LinkList* list, int pos);
    
    LinkListNode* LinkList_Delete(LinkList* list, int pos);
    
    #endif
    
    

    (*.c)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "linklist.h"
    
    typedef struct _tag_LinkList
    {
    	LinkListNode header;
    	int length;
    }TLinkList;
    
    LinkList* LinkList_Create()
    {
    	TLinkList *ret = (TLinkList *)malloc(sizeof(TLinkList));
    	if (ret == NULL)
    	{
    		return NULL;
    	}
    	//memset(ret, 0, sizeof(TLinkList));
    	ret->header.next = NULL;
    	ret->length = 0;
    	return ret;
    }
    
    void LinkList_Destroy(LinkList* list)
    {
    	if (list == NULL)
    	{
    		return;
    	}
    	free(list);
    	return;
    }
    
    void LinkList_Clear(LinkList* list)
    {
    	TLinkList *tList = NULL;
    
    	if (list == NULL)
    	{
    		return;
    	}
    	tList = (TLinkList *)list;
    	tList->length = 0;
    	tList->header.next = NULL;
    	return;
    }
    
    int LinkList_Length(LinkList* list)
    {
    
    	TLinkList *tList = (TLinkList *)list;
    	if (tList == NULL)
    	{
    		return -1;
    	}
    
    	return tList->length;
    }
    
    int LinkList_Insert(LinkList* list, LinkListNode* node, int pos)
    {
    	int				i = 0;
    	TLinkList		*tList = NULL;
    	LinkListNode	*current = NULL;
    
    	tList = (TLinkList *)list;
    	//准备环境让辅助指针变量 指向链表头节点
    	current = &tList->header;
    	for (i = 0; i < pos && (current->next != NULL); i++)
    	{
    		current = current->next;
    	}
    	//让node节点链接后续链表
    	node->next = current->next;
    	//让前边的链表。链接node
    	current->next = node;
    	tList->length++;
    	return 0;
    }
    
    LinkListNode* LinkList_Get(LinkList* list, int pos)
    {
    	int				i = 0;
    	TLinkList		*tList = NULL;
    	LinkListNode	*current = NULL;
    	LinkListNode	*ret = NULL;
    
    	tList = (TLinkList *)list;
    
    	if (list == NULL || pos < 0 || pos >= tList->length)
    	{
    		return NULL;
    	}
    	//准备环境让辅助指针变量 指向链表头节点
    	current = &tList->header;
    	for (i = 0; i < pos && (current->next != NULL); i++)
    	{
    		current = current->next;
    	}
    	ret = current->next;
    
    	return ret;
    }
    
    LinkListNode* LinkList_Delete(LinkList* list, int pos)
    {
    	int				i = 0;
    	TLinkList		*tList = NULL;
    	LinkListNode	*current = NULL;
    	LinkListNode	*ret = NULL;
    
    	tList = (TLinkList *)list;
    
    	if (list == NULL || pos < 0 || pos >= tList->length)
    	{
    		return NULL;
    	}
    	//准备环境让辅助指针变量 指向链表头节点
    	current = &tList->header;
    	for (i = 0; i < pos && (current->next != NULL); i++)
    	{
    		current = current->next;
    	}
    	ret = current->next;
    	//删除算法
    	current->next = ret->next;
    	tList->length--;
    	return ret;
    }
    

    (test)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "linklist.h"
    
    typedef struct Teacher
    {
    	LinkListNode node;
    	char name[64];
    	int age;
    }Teacher;
    
    int main()
    {
    	Teacher		t1, t2, t3;
    	int			length, i = 0;
    	LinkList	*list = NULL;
    
    	t1.age = 31;
    	t2.age = 32;
    	t3.age = 33;
    
    	list = LinkList_Create();
    	length = LinkList_Length(list);
    
    	LinkList_Insert(list, (LinkListNode *)&t1, LinkList_Length(list));
    	LinkList_Insert(list, (LinkListNode *)&t2, LinkList_Length(list));
    	LinkList_Insert(list, (LinkListNode *)&t3, LinkList_Length(list));
    
    	//遍历链表 
    	for (i = 0; i < LinkList_Length(list); i++)
    	{
    		Teacher *tmp = (Teacher *)LinkList_Get(list, i);
    		if (tmp != NULL)
    		{
    			printf("age:%d ", tmp->age);
    		}
    	}
    	while (LinkList_Length(list) > 0)
    	{
    		Teacher *tmp = (Teacher *)LinkList_Delete(list, 0);
    		if (tmp != NULL)
    		{
    			printf("age:%d ", tmp->age);
    		}
    	}
    	LinkList_Destroy(list);
    	system("pause");
    	return 0;
    }
    

    优点和缺点

    • 优点:
      无需一次性定制链表的容量
      插入和删除操作无需移动数据元素

    • 缺点:
      数据元素必须保存后继元素的位置信息
      获取指定数据的元素操作需要顺序访问之前的元素

    循环链表

    基本概念

    将单链表中最后一个数据元素的next指针指向第一个元素
    循环链表
    循环链表拥有单链表的所有操作

    • 创建链表
    • 销毁链表
    • 获取链表长度
    • 清空链表
    • 获取第pos个元素操作
    • 插入元素到位置pos
    • 删除位置pos处的元素
    • 新增功能:游标的定义

    在循环链表中可以定义一个“当前”指针,这个指针通常称为游标,可以通过这个游标来遍历链表中的所有元素
    循环链表游标
    获取当前游标指向的数据元素
    将游标重置指向链表中的第一个数据元素
    将游标移动指向到链表中的下一个数据元素
    直接指定删除链表中的某个数据元素

    CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);
    CircleListNode* CircleList_Reset(CircleList* list);
    CircleListNode* CircleList_Current(CircleList* list);
    CircleListNode* CircleList_Next(CircleList* list);
    

    设计与实现

    • 插入元素的分析
      • 普通位置插入元素
      • 添加第一个元素(第一次插入元素)
      • 最后一个位置插入元素
      • 第一个位置插入元素

    (*.h)

    #ifndef __CIRCLELIST_H_
    #define __CIRCLELIST_H_
    
    typedef void CircleList;
    /*
    typedef struct _tag_CircleListNode CircleListNode;
    struct _tag_CircleListNode
    {
    	CircleListNode* next;
    };
    */
    typedef struct _tag_CircleListNode
    {
    	struct _tag_CircleListNode * next;
    }CircleListNode;
    
    CircleList* CircleList_Create();
    
    void CircleList_Destroy(CircleList* list);
    
    void CircleList_Clear(CircleList* list);
    
    int CircleList_Length(CircleList* list);
    
    int CircleList_Insert(CircleList* list, CircleListNode* node, int pos);
    
    CircleListNode* CircleList_Get(CircleList* list, int pos);
    
    CircleListNode* CircleList_Delete(CircleList* list, int pos);
    
    //add
    CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);
    
    CircleListNode* CircleList_Reset(CircleList* list);
    
    CircleListNode* CircleList_Current(CircleList* list);
    
    CircleListNode* CircleList_Next(CircleList* list);
    
    #endif
    

    (*.c)

    #include <stdio.h>
    #include <malloc.h>
    
    #include "CircleList.h"
    
    typedef struct _tag_CircleList
    {
    	CircleListNode header;
    	CircleListNode* slider;
    	int length;
    } TCircleList;
    
    CircleList* CircleList_Create() // O(1)
    {
    	TCircleList* ret = (TCircleList*)malloc(sizeof(TCircleList));
    	if (ret == NULL)
    	{
    		return NULL;
    	}
    	ret->length = 0;
    	ret->header.next = NULL;
    	ret->slider = NULL;
    	return ret;
    }
    
    void CircleList_Destroy(CircleList* list) // O(1)
    {
    	if (list == NULL)
    	{
    		return;
    	}
    	free(list);
    }
    
    void CircleList_Clear(CircleList* list) // O(1)
    {
    	TCircleList* sList = (TCircleList*)list;
    	if (sList == NULL)
    	{
    		return;
    	}
    	sList->length = 0;
    	sList->header.next = NULL;
    	sList->slider = NULL;
    }
    
    int CircleList_Length(CircleList* list) // O(1)
    {
    	TCircleList* sList = (TCircleList*)list;
    	int ret = -1;
    	if (list == NULL)
    	{
    		return ret;
    	}
    	ret = sList->length;
    	return ret;
    }
    
    int CircleList_Insert(CircleList* list, CircleListNode* node, int pos) // O(n)
    {
    	int				ret = 0, i = 0;
    	TCircleList*	sList = (TCircleList*)list;
    	if (list == NULL || node == NULL || pos < 0)
    	{
    		return -1;
    	}
    	CircleListNode* current = (CircleListNode*)sList;
    	for (i = 0; (i < pos) && (current->next != NULL); i++)
    	{
    		current = current->next;
    	}
    	//current->next 0号节点的地址
    	node->next = current->next; //1
    	current->next = node; //2
    
    	//若第一次插入节点
    	if (sList->length == 0)
    	{
    		sList->slider = node;
    	}
    	sList->length++;
    	//若头插法
    	if (current == (CircleListNode*)sList)
    	{
    		//获取最后一个元素
    		CircleListNode* last = CircleList_Get(sList, sList->length - 1);
    		last->next = current->next; //3
    
    	}
    	return ret;
    }
    
    CircleListNode* CircleList_Get(CircleList* list, int pos) // O(n)
    {
    	TCircleList*		sList = (TCircleList*)list;
    	CircleListNode*		ret = NULL;
    	int					i = 0;
    	if (list == NULL || pos < 0)
    	{
    		return NULL;
    	}
    	//if( (sList != NULL) && (pos >= 0) && (sList->length > 0) )
    	{
    		CircleListNode* current = (CircleListNode*)sList;
    
    		for (i = 0; i < pos; i++)
    		{
    			current = current->next;
    		}
    
    		ret = current->next;
    	}
    	return ret;
    }
    
    CircleListNode* CircleList_Delete(CircleList* list, int pos) // O(n)
    {
    	TCircleList* sList = (TCircleList*)list;
    	CircleListNode* ret = NULL;
    	int i = 0;
    	if ((sList != NULL) && (pos >= 0) && (sList->length > 0))
    	{
    		CircleListNode* current = (CircleListNode*)sList;
    		CircleListNode* last = NULL;
    		for (i = 0; i < pos; i++)
    		{
    			current = current->next;
    		}
    		//若删除第一个元素
    		if (current == (CircleListNode*)sList)
    		{
    			last = (CircleListNode*)CircleList_Get(sList, sList->length - 1);
    		}
    		//求要删除的元素
    		ret = current->next;
    		current->next = ret->next;
    		sList->length--;
    		//判断链表是否为空
    		if (last != NULL)
    		{
    			sList->header.next = ret->next;
    			last->next = ret->next;
    		}
    		//若删除的元素为游标所指的元素
    		if (sList->slider == ret)
    		{
    			sList->slider = ret->next;
    		}
    		//若删除元素后,链表长度为0
    		if (sList->length == 0)
    		{
    			sList->header.next = NULL;
    			sList->slider = NULL;
    		}
    	}
    	return ret;
    }
    
    CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node) // O(n)
    {
    	TCircleList* sList = (TCircleList*)list;
    	CircleListNode* ret = NULL;
    	int i = 0;
    	if (sList != NULL)
    	{
    		CircleListNode* current = (CircleListNode*)sList;
    		//查找node在循环链表中的位置i
    		for (i = 0; i < sList->length; i++)
    		{
    			if (current->next == node)
    			{
    				ret = current->next;
    				break;
    			}
    			current = current->next;
    		}
    		//如果ret找到,根据i去删除	
    		if (ret != NULL)
    		{
    			CircleList_Delete(sList, i);
    		}
    	}
    	return ret;
    }
    
    CircleListNode* CircleList_Reset(CircleList* list) // O(1)
    {
    	TCircleList*		sList = (TCircleList*)list;
    	CircleListNode*		ret = NULL;
    	if (sList != NULL)
    	{
    		sList->slider = sList->header.next;
    		ret = sList->slider;
    	}
    	return ret;
    }
    
    CircleListNode* CircleList_Current(CircleList* list) // O(1)
    {
    	TCircleList*		sList = (TCircleList*)list;
    	CircleListNode*		ret = NULL;
    	if (sList != NULL)
    	{
    		ret = sList->slider;
    	}
    	return ret;
    }
    
    CircleListNode* CircleList_Next(CircleList* list) // O(1)
    {
    	TCircleList* sList = (TCircleList*)list;
    	CircleListNode* ret = NULL;
    	if ((sList != NULL) && (sList->slider != NULL))
    	{
    		ret = sList->slider;
    		sList->slider = ret->next;
    	}
    	return ret;
    }
    

    (test)

    #include <stdio.h>
    #include <stdlib.h>
    
    #include "CircleList.h"
    
    struct Value
    {
    	CircleListNode circlenode;
    	int v;
    };
    
    int main()
    {
    	int				i = 0;
    	CircleList*		list = CircleList_Create();
    
    	struct Value v1;
    	struct Value v2;
    	struct Value v3;
    	struct Value v4;
    	struct Value v5;
    	struct Value v6;
    	struct Value v7;
    	struct Value v8;
    
    	v1.v = 1;
    	v2.v = 2;
    	v3.v = 3;
    	v4.v = 4;
    	v5.v = 5;
    	v6.v = 6;
    	v7.v = 7;
    	v8.v = 8;
    
    	CircleList_Insert(list, (CircleListNode*)&v1, 0);
    	CircleList_Insert(list, (CircleListNode*)&v2, 0);
    	CircleList_Insert(list, (CircleListNode*)&v3, 0);
    	CircleList_Insert(list, (CircleListNode*)&v4, 0);
    
    	for (i = 0; i < 2 * CircleList_Length(list); i++)
    	{
    		struct Value* pv = (struct Value*)CircleList_Get(list, i);
    		printf("%d
    ", pv->v);
    	}
    
    	while (CircleList_Length(list) > 0)
    	{
    		CircleList_Delete(list, 0);
    	}
    	printf("
    ");
    
    	CircleList_Destroy(list);
    
    	system("pause");
    	return 0;
    }
    

    优点和缺点

    • 优点:功能强了。
      循环链表只是在单链表的基础上做了一个加强
      循环链表可以完全取代单链表的使用
      循环链表的Next和Current操作可以高效的遍历链表中的所有元素

    • 缺点:
      代码复杂度提高了

    约瑟夫问题-循环链表典型应用
    n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数,报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报到第 m 个人,再令其出列,···,如此下去,求出列顺序
    循环链表约瑟夫问题

    #include <stdio.h>
    #include <stdlib.h>
    
    #include "CircleList.h"
    
    struct Value
    {
    	CircleListNode header;
    	int v;
    };
    
    void main()
    {
    	int				i = 0;
    	CircleList*		list = CircleList_Create();
    
    	struct Value v1, v2, v3, v4, v5, v6, v7, v8;
    
    	v1.v = 1;	v2.v = 2;	v3.v = 3;	v4.v = 4;
    	v5.v = 5;	v6.v = 6;	v7.v = 7;	v8.v = 8;
    
    	CircleList_Insert(list, (CircleListNode*)&v1, CircleList_Length(list));
    	CircleList_Insert(list, (CircleListNode*)&v2, CircleList_Length(list));
    	CircleList_Insert(list, (CircleListNode*)&v3, CircleList_Length(list));
    	CircleList_Insert(list, (CircleListNode*)&v4, CircleList_Length(list));
    	CircleList_Insert(list, (CircleListNode*)&v5, CircleList_Length(list));
    	CircleList_Insert(list, (CircleListNode*)&v6, CircleList_Length(list));
    	CircleList_Insert(list, (CircleListNode*)&v7, CircleList_Length(list));
    	CircleList_Insert(list, (CircleListNode*)&v8, CircleList_Length(list));
    
    	for (i = 0; i < CircleList_Length(list); i++)
    	{
    		struct Value* pv = (struct Value*)CircleList_Next(list);
    		printf("%d
    ", pv->v);
    	}
    	printf("
    ");
    
    	//重置游标
    	CircleList_Reset(list);
    	while (CircleList_Length(list) > 0)
    	{
    		struct Value* pv = NULL;
    		for (i = 1; i < 3; i++)
    		{
    			CircleList_Next(list);
    		}
    		pv = (struct Value*)CircleList_Current(list);
    		printf("%d
    ", pv->v);
    		CircleList_DeleteNode(list, (CircleListNode*)pv);
    	}
    	CircleList_Destroy(list);
    
    	system("pause");
    }
    

    双向链表

    基本概念

    单链表的结点都只有一个指向下一个结点的指针
    单链表的数据元素无法直接访问其前驱元素
    逆序访问单链表中的元素是极其耗时的操作

    len = LinkList_Length(list);
    for (i=len-1; len>=0; i++) //O(n)
    {
    LinkListNode *p = LinkList_Get(list, i); //O(n)
    //访问数据元素p中的元素
    }
    

    双向链表的定义
    在单链表的结点中增加一个指向其前驱的pre指针
    双向链表
    双向链表拥有单链表的所有操作

    • 创建链表
    • 销毁链表
    • 获取链表长度
    • 清空链表
    • 获取第pos个元素操作
    • 插入元素到位置pos
    • 删除位置pos处的元素

    设计与实现

    • 插入操作
      双向链表插入操作

    • 删除操作
      双向链表删除操作
      双向链表的新操作
      获取当前游标指向的数据元素
      将游标重置指向链表中的第一个数据元素
      将游标移动指向到链表中的下一个数据元素
      将游标移动指向到链表中的上一个数据元素
      直接指定删除链表中的某个数据元素

    DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node);
    DLinkListNode* DLinkList_Reset(DLinkList* list);
    DLinkListNode* DLinkList_Current(DLinkList* list);
    DLinkListNode* DLinkList_Next(DLinkList* list);
    DLinkListNode* DLinkList_Pre(DLinkList* list);
    

    (*.h)

    #ifndef __MY_DLINKLIST_H_
    #define __MY_DLINKLIST_H_
    
    typedef void DLinkList;
    /*
    typedef struct _tag_DLinkListNode DLinkListNode;
    struct _tag_DLinkListNode
    {
    DLinkListNode* next;
    DLinkListNode* pre;
    };
    */
    
    typedef struct _tag_DLinkListNode
    {
    	struct _tag_DLinkListNode* next;
    	struct _tag_DLinkListNode * pre;
    }DLinkListNode;
    
    DLinkList* DLinkList_Create();
    
    void DLinkList_Destroy(DLinkList* list);
    
    void DLinkList_Clear(DLinkList* list);
    
    int DLinkList_Length(DLinkList* list);
    
    int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos);
    
    DLinkListNode* DLinkList_Get(DLinkList* list, int pos);
    
    DLinkListNode* DLinkList_Delete(DLinkList* list, int pos);
    
    //add
    DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node);
    
    DLinkListNode* DLinkList_Reset(DLinkList* list);
    
    DLinkListNode* DLinkList_Current(DLinkList* list);
    
    DLinkListNode* DLinkList_Next(DLinkList* list);
    
    DLinkListNode* DLinkList_Pre(DLinkList* list);
    
    #endif
    

    (*.c)

    #include <stdio.h>
    #include <malloc.h>
    
    #include "DLinkList.h"
    
    typedef struct _tag_DLinkList
    {
    	DLinkListNode header;
    	DLinkListNode* slider;
    	int length;
    } TDLinkList;
    
    DLinkList* DLinkList_Create()
    {
    	TDLinkList* ret = (TDLinkList*)malloc(sizeof(TDLinkList));
    
    	if (ret != NULL)
    	{
    		ret->length = 0;
    		ret->header.next = NULL;
    		ret->header.pre = NULL;
    		ret->slider = NULL;
    	}
    
    	return ret;
    }
    
    void DLinkList_Destroy(DLinkList* list)
    {
    	if (list != NULL)
    	{
    		free(list);
    	}
    }
    
    void DLinkList_Clear(DLinkList* list)
    {
    	TDLinkList* sList = (TDLinkList*)list;
    
    	if (sList != NULL)
    	{
    		sList->length = 0;
    		sList->header.next = NULL;
    		sList->header.pre = NULL;
    		sList->slider = NULL;
    	}
    }
    
    int DLinkList_Length(DLinkList* list)
    {
    	int				ret = -1;
    	TDLinkList*		sList = (TDLinkList*)list;
    
    	if (sList != NULL)
    	{
    		ret = sList->length;
    	}
    
    	return ret;
    }
    
    //大家一定要注意:教科书不会告诉你 项目上如何用;哪些点是项目的重点
    int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos)
    {
    	int				ret = 0, i = 0;
    	TDLinkList*		sList = (TDLinkList*)list;
    
    	if (list == NULL || node == NULL || pos < 0)
    	{
    		return -1;
    	}
    
    	{
    		DLinkListNode* current = (DLinkListNode*)sList;
    		DLinkListNode* next = NULL; //需要增加next指针
    
    		for (i = 0; (i < pos) && (current->next != NULL); i++)
    		{
    			current = current->next;
    		}
    
    		next = current->next;
    
    		//步骤1-2
    		current->next = node;
    		node->next = next;
    
    		//步骤3-4 
    		if (next != NULL) //当链表插入第一个元素,需要特殊处理
    		{
    			next->pre = node;
    		}
    		node->pre = current;
    
    		if (sList->length == 0)
    		{
    			sList->slider = node; //当链表插入第一个元素处理游标
    		}
    
    		//若在0位置插入,需要特殊处理 新来结点next前pre指向null
    		if (current == (DLinkListNode*)sList)
    		{
    			node->pre = NULL;
    		}
    
    		sList->length++;
    	}
    
    	return ret;
    }
    
    DLinkListNode* DLinkList_Get(DLinkList* list, int pos)
    {
    	int				i = 0;
    	TDLinkList*		sList = (TDLinkList*)list;
    	DLinkListNode*	ret = NULL;
    
    	if ((sList != NULL) && (0 <= pos) && (pos < sList->length))
    	{
    		DLinkListNode* current = (DLinkListNode*)sList;
    
    		for (i = 0; i < pos; i++)
    		{
    			current = current->next;
    		}
    
    		ret = current->next;
    	}
    
    	return ret;
    }
    
    //插入第一个节点
    //删除的是最后一个结点,该是如何处理
    DLinkListNode* DLinkList_Delete(DLinkList* list, int pos)
    {
    	int				i = 0;
    	TDLinkList*		sList = (TDLinkList*)list;
    	DLinkListNode*	ret = NULL;
    
    	if (sList == NULL || pos < 0)
    	{
    		return NULL;
    	}
    	//if( (sList != NULL) && (0 <= pos) && (pos < sList->length) )
    	{
    		DLinkListNode* current = (DLinkListNode*)sList;
    		DLinkListNode* next = NULL; //需要增加next指针
    
    		for (i = 0; i < pos; i++)
    		{
    			current = current->next;
    		}
    
    		ret = current->next;
    		next = ret->next;
    
    		//步骤1
    		current->next = next;
    
    		//步骤2 
    		if (next != NULL)//需要特殊处理
    		{
    			next->pre = current;
    
    			if (current == (DLinkListNode*)sList) //若第0个位置,需要特殊处理
    			{
    				next->pre = NULL;
    			}
    		}
    
    		if (sList->slider == ret)
    		{
    			sList->slider = next;
    		}
    
    		sList->length--;
    	}
    
    	return ret;
    }
    
    DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node)
    {
    	int				i = 0;
    	TDLinkList*		sList = (TDLinkList*)list;
    	DLinkListNode*	ret = NULL;
    
    	if (sList != NULL)
    	{
    		DLinkListNode* current = (DLinkListNode*)sList;
    
    		for (i = 0; i < sList->length; i++)
    		{
    			if (current->next == node)
    			{
    				ret = current->next;
    				break;
    			}
    
    			current = current->next;
    		}
    
    		if (ret != NULL)
    		{
    			DLinkList_Delete(sList, i);
    		}
    	}
    
    	return ret;
    }
    
    DLinkListNode* DLinkList_Reset(DLinkList* list)
    {
    	TDLinkList*		sList = (TDLinkList*)list;
    	DLinkListNode*	ret = NULL;
    
    	if (sList != NULL)
    	{
    		sList->slider = sList->header.next;
    		ret = sList->slider;
    	}
    
    	return ret;
    }
    
    DLinkListNode* DLinkList_Current(DLinkList* list)
    {
    	TDLinkList*		sList = (TDLinkList*)list;
    	DLinkListNode*	ret = NULL;
    
    	if (sList != NULL)
    	{
    		ret = sList->slider;
    	}
    
    	return ret;
    }
    
    DLinkListNode* DLinkList_Next(DLinkList* list)
    {
    	TDLinkList*		sList = (TDLinkList*)list;
    	DLinkListNode*	ret = NULL;
    
    	if ((sList != NULL) && (sList->slider != NULL))
    	{
    		ret = sList->slider;
    		sList->slider = ret->next;
    	}
    
    	return ret;
    }
    
    DLinkListNode* DLinkList_Pre(DLinkList* list)
    {
    	TDLinkList*		sList = (TDLinkList*)list;
    	DLinkListNode*	ret = NULL;
    
    	if ((sList != NULL) && (sList->slider != NULL))
    	{
    		ret = sList->slider;
    		sList->slider = ret->pre;
    	}
    	return ret;
    }
    

    (test)

    #include <stdio.h>
    #include <stdlib.h>
    
    #include "DLinkList.h"
    
    struct Value
    {
    	DLinkListNode node;
    	int v;
    };
    
    int main()
    {
    	int i = 0;
    	DLinkList* list = DLinkList_Create();
    	struct Value* pv = NULL;
    	struct Value v1, v2, v3, v4, v5;
    
    	v1.v = 1;	v2.v = 2;	v3.v = 3;
    	v4.v = 4;	v5.v = 5;
    
    	DLinkList_Insert(list, (DLinkListNode*)&v1, DLinkList_Length(list));
    	DLinkList_Insert(list, (DLinkListNode*)&v2, DLinkList_Length(list));
    	DLinkList_Insert(list, (DLinkListNode*)&v3, DLinkList_Length(list));
    	DLinkList_Insert(list, (DLinkListNode*)&v4, DLinkList_Length(list));
    	DLinkList_Insert(list, (DLinkListNode*)&v5, DLinkList_Length(list));
    
    	for(i=0; i<DLinkList_Length(list); i++)
    	{
    		pv = (struct Value*)DLinkList_Get(list, i);
    		printf("%d
    ", pv->v);
    	}
    	printf("
    ");
    
    	DLinkList_Delete(list, DLinkList_Length(list)-1);
    	DLinkList_Delete(list, 0);
    	//DLinkList_Delete(list, 3);
    
    	for(i=0; i<DLinkList_Length(list); i++)
    	{
    		pv = (struct Value*)DLinkList_Next(list);
    		printf("%d
    ", pv->v);
    	}
    	printf("
    ");
    
    	DLinkList_Reset(list);
    	DLinkList_Next(list);
    
    	pv = (struct Value*)DLinkList_Current(list);
    	printf("%d
    ", pv->v);
    
    	DLinkList_DeleteNode(list, (DLinkListNode*)pv);
    
    	pv = (struct Value*)DLinkList_Current(list);
    	printf("%d
    ", pv->v);
    
    	DLinkList_Pre(list);
    	pv = (struct Value*)DLinkList_Current(list);
    	printf("%d
    ", pv->v);
    
    	printf("Length: %d
    ", DLinkList_Length(list));
    
    	DLinkList_Destroy(list);
    
    	system("pause");
    	return 0;
    }
    

    优点和缺点

    • 优点:双向链表在单链表的基础上增加了指向前驱的指针
      功能上双向链表可以完全取代单链表的使用
      循环链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素

    • 缺点:代码复杂

  • 相关阅读:
    Ansible import_playbook 导入playbook
    Ansible Playbook 变量传递
    Ansible Notify与Handler组合
    Ansible Role
    Ansible infile模块
    Ansible When有条件的运行任务
    Ansible register捕获命令输出
    Ansible Command Shell Raw模块区别
    Ansible Facts
    Ansible Jinjia2模块
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664711.html
Copyright © 2011-2022 走看看