线性表学习笔记

第二章 线性表

作者有话说:这笔记主要是面对研究生考试的,程序员面试也可以参考,等我有空一定把双向链表和静态链表补了,再更完第五节

2.1 线性表的定义和基本操作

1.线性表的定义

  • 相同数据类型的n个数据元素的有限序列

  • a1是唯一的表头元素,an是唯一的表尾元素

  • 每个元素有一个前驱元素一个后继元素

2. 线性表的特点

  • 表中的元素个数有限
  • 表中的元素具有逻辑上的顺序性
  • 表中的元素都是数据元素,每个元素都是单个元素
  • 表中的元素数据结构类型都相同,每个元素占同样大小的存储空间
  • 表中的元素具有抽象性,只讨论元素之间的逻辑关系不讨论内容
  • 注意:线性表是一种逻辑结构,表示一一对应的相邻关系,顺序表和链表是存储结构,两者不一样

3.线性表的基本操作

//1.初始化线性表:构造一个空的线性表
void InitList(LinerList& L); 
//2.求表长:返回线性表L的长度,也就是L中数据元素的个数
int GetLength(LinerList L);
//3.按值查找操作:再表L中查找具有给定给定关键字值的元素,返回这个元素
ELEMENT LocateElem(LinerList L, int e);
//4.按位查找操作:获取表L中的第i个位置上插入指定元素e
ELEMENT GetElement(LinerList L, int i);
//5.插入操作:在L中的第i个位置上插入指定元素e
bool ListInsert(LinerList& L, int i, ELEMENT e);
//6.删除操作:删除L第i个位置的元素,并返回删除元素的值
bool ListDelete(LinerList& L, int i, ELEMENT& e);
//7.输出操作:按前后顺序输出线性表L的所有元素值
void PrintList(LinerList L);
//8.判空操作:判断L是否为空, 若是返回true,否则返回false
bool isEmpty(LinerList L);
//9.销毁操作:销毁线性表,并释放线性表L所占的存储空间
void DestroyList(LinerList& L);

2.2 顺序表

1.顺序表的定义

  • 顺序表:用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻

  • 第1个元素放在0号位置上

#define MAXSIZE 1000//定义最大值
typedef  int  ELEMENT;  //定义顺序表中的数据类型,保证能存任何数据类型
//定义线性表
typedef struct LinkList {
	ELEMENT* list;//定义指向线性表的指针
	ELEMENT MaxLen, Len;//定义线性表的最大长度,当前长度
}LinerList;

2. 线性表的特点

  • 随机访问。首地址和元素序号可以在时间O(1)内找到指定元素
  • 存储密度高。每个节点只存储数据元素
  • 逻辑上相邻的元素物理上也相邻,所以插入和删除要移动大量元素
  • 数组的大小不好确定
  • 连续占用一大段空间,造成很多碎片

3. 插入操作

在L中的第i个位置上插入指定元素e

bool ListInsert(LinerList& L, int i, ELEMENT e)
{
	//判断插入的位置是否合法,注意,插入的元素是第一个元素到第len+1个元素
	if (i<1 || i>L.Len+1)
	{
		printf("The insertion position is illegal\n");
		return false;
	}
	//判断链表是否已满
	if (L.Len >= L.MaxLen)
	{
		printf("The list is full\n");
		return false;
	}
	//插入,在链表的第i个位置上插入e
	int j = 0;
	for (j = L.Len; j >= i; j--)
	{
		L.list[j] = L.list[j - 1];
	}
	L.list[j] = e;
	L.Len++;
	return true;
}

时间复杂度为O(n)

4.删除操作

删除L第i个位置的元素,并返回删除元素的值

bool ListDelete(LinerList& L, int i, ELEMENT& e)
{
	//判断位置是否合法
	if (i < 1 || i > L.Len)
	{
		printf("the location you delete is illegal");
		return false;
	}
	//判断链表是否为空
	if (L.Len==0)
	{
		printf("list is empty\n");
		return false;
	}
	//删除
	e = L.list[i - 1];
	for (int j = i - 1; j < L.Len - 1; j++)
		L.list[j] = L.list[j + 1];
	L.Len--;
	return true;
}

最好情况直接插入到表尾,时间复杂度为O(1)

最坏情况插入到表头,时间复杂度为O(n)

平均情况n/2

时间复杂度O(n)

5.按值查找

//3.按值查找操作:再表L中查找具有给定给定关键字值的元素,返回位置
int LocateElem(LinerList L, int e)
{
	//若存在返回对应的位置
	for (int i = 0; i < GetLength(L); i++)
	{
		if (L.list[i] == e)
			return i+1;
	}
	//不存在返回0
	return 0;
}

时间复杂度O(n)

6.顺序表的实现

/*
实现线性表的顺序表示
*/

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

#define MAXSIZE 1000//定义最大值
typedef  int  ELEMENT;  //定义顺序表中的数据类型,保证能存任何数据类型
//定义线性表
typedef struct LinkList {
	ELEMENT* list;//定义指向线性表的指针
	ELEMENT MaxLen, Len;//定义线性表的最大长度,当前长度
}LinerList;

void InitList(LinerList& L); //1.初始化线性表:构造一个空的线性表
int GetLength(LinerList L);//2.求表长:返回线性表L的长度,也就是L中数据元素的个数
ELEMENT LocateElem(LinerList L, int e);//3.按值查找操作:再表L中查找具有给定给定关键字值的元素,返回这个元素
ELEMENT GetElement(LinerList L, int i);//4.按位查找操作:获取表L中的第i个位置上插入指定元素e
bool ListInsert(LinerList& L, int i, ELEMENT e);//5.插入操作:在L中的第i个位置上插入指定元素e
bool ListDelete(LinerList& L, int i, ELEMENT& e);//6.删除操作:删除L第i个位置的元素,并返回删除元素的值
void PrintList(LinerList L);//7.输出操作:按前后顺序输出线性表L的所有元素值
bool isEmpty(LinerList L);//8.判空操作:判断L是否为空, 若是返回true,否则返回false
void DestroyList(LinerList& L);//9.销毁操作:销毁线性表,并释放线性表L所占的存储空间

int main()
{
	LinerList L;
	InitList(L);//初始化链表
	printf("%d", GetLength(L));
	for (int i = 0; i < 10; i++)
	{
		L.list[L.Len++] = i + 100;
	}
	PrintList(L);
	ListInsert(L, 3, 66);//在第三个位置插入66
	ELEMENT e;
	ListDelete(L, 2,e);//删除第二个元素
	printf("the element you delete is:%d", e);
	PrintList(L);
	int res=LocateElem(L, 66);//找有没有值为66的数字
	printf("%d", res);
	DestroyList(L);
	return 0;
}

//1.初始化线性表:构造一个空的线性表
void InitList(LinerList& L)
{
	L.list = (ELEMENT*)malloc(sizeof(ELEMENT) * MAXSIZE);
	L.MaxLen = MAXSIZE;
	L.Len = 0;
}

//2.求表长:返回线性表L的长度,也就是L中数据元素的个数
int GetLength(LinerList L)
{
	return L.Len;
}

//3.按值查找操作:再表L中查找具有给定给定关键字值的元素,返回位置
int LocateElem(LinerList L, int e)
{
	//若存在返回对应的位置
	for (int i = 0; i < GetLength(L); i++)
	{
		if (L.list[i] == e)
			return i+1;
	}
	//不存在返回0
	return 0;
}


//4.按位查找操作:获取表L中的第i个位置上插入指定元素e
ELEMENT GetElement(LinerList L, int i)
{
	return L.list[i];
}

//5.插入操作:在L中的第i个位置上插入指定元素e
bool ListInsert(LinerList& L, int i, ELEMENT e)
{
	//判断插入的位置是否合法,注意,插入的元素是第一个元素到第len+1个元素
	if (i<1 || i>GetLength(L)+1)
	{
		printf("The insertion position is illegal\n");
		return false;
	}
	//判断链表是否已满
	if (L.Len >= L.MaxLen)
	{
		printf("The list is full\n");
		return false;
	}
	//插入,在链表的第i个位置上插入e
	int j = 0;
	for (j = L.Len; j >= i; j--)
	{
		L.list[j] = L.list[j - 1];
	}
	L.list[j] = e;
	L.Len++;
	return true;
}

//6.删除操作:删除L第i个位置的元素,并返回删除元素的值
bool ListDelete(LinerList& L, int i, ELEMENT& e)
{
	//判断位置是否合法
	if (i < 1 || i > L.Len)
	{
		printf("the location you delete is illegal");
		return false;
	}
	//判断链表是否为空
	if (isEmpty(L))
	{
		printf("list is empty\n");
		return false;
	}
	//删除
	e = L.list[i - 1];
	for (int j = i - 1; j < L.Len - 1; j++)
		L.list[j] = L.list[j + 1];
	L.Len--;
	return true;

}

//7.输出操作,按前后顺序输出线性表L的所有元素值
void PrintList(LinerList L)
{
	printf("\nall the elements of list:\n");
	for (int i = 0; i < GetLength(L); i++)
		printf("%d ", L.list[i]);
	printf("\n");
}

//8.判空操作:判断L是否为空, 若是返回true,否则返回false
bool isEmpty(LinerList L)
{
	return GetLength(L) == 0;
}

//9.销毁操作:销毁线性表,并释放线性表L所占的存储空间
void DestroyList(LinerList& L)
{
	free(L.list);
}

2.3链表

顺序表可以随机存取任意一个元素,因为他的元素存放的位置可以通过地址算出来,但是链表不使用地址连续的存储单元,他通过链来建立数据之间的逻辑关系,因此插入和删除不需要移动元素只需要改指向

2.3.1单链表

1.概述
  • 定义

单链表是通过一组任意存储单元来存储线性表中的数据元素。每个节点除了存放自己的信息还记录下一个节点的地址,data为数据域用来存放数据,next为指针域,用来存放后继节点的地址

//定义单链表的节点
typedef struct Node {
	ElementType Data;//数据域
	struct Node* next;//指针域
}Node;
  • 特点

单链表需要额外的空间放指针域,但是离散地分布在存储空间中,是非随机存储的结构,查找某个节点需要从头开始遍历

  • 头指针和头节点

头指针指向链表的第一个节点,用来标记链表,头节点是指链表的第一个节点,一般会不带信息或者带链表的长度。

//定义头指针head,head指向头节点
	Node* head = (Node*)malloc(sizeof(Node));
	head->next = NULL;
	head->Data = 0;	
	Node* head = (Node*)malloc(sizeof(Node));
	head->next = NULL;
	head->Data = 0;	
2.基本操作
//1.头插法建立单链表,传入头指针L,把节点依次插入到都头结点之后
bool ListHeadInsert(Node*& LHead);
//2.尾插法建立单链表,将新节点插入到链表的表尾
bool ListTailInsert(Node*& LHead);
//3.按序号查找节点值:找到第i个节点,返回指向这个节点的指针
Node* GetElem(Node* LHead, int index);
//4.按值查找节点,找到值为value的节点,返回节点的位置
int LocateElem(Node* LHead, ElementType value);
//5.插入节点操作:将值为value的节点插入到第index个位置上
bool NodeInsert(Node*& LHead, ElementType value,int index);
//6.删除节点操作:删除第i个节点
bool NodeDelete(Node*& LHead, int index);
//7.求表长操作:求表的长度
int GetListLen(Node* LHead);
//8.打印链表
void PrintList(Node* LHead);
//9.释放链表
void DestroyList(Node*& LHead);
头插法
//1.头插法建立单链表,传入头指针L,把节点依次插入到都头结点之后
bool ListHeadInsert(Node*& LHead)
{
	//读入要插入的节点以9999结束
	int i = 0;
	int nums[] = { 1,2,3,4,5,6,9999 };
	int num = nums[i++];
		
	while (num != 9999)
	{
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->Data = num;
		newNode->next = LHead->next;
		LHead->next = newNode;

		num = nums[i++];
	}	
	return true;
}

尾插法
//2.尾插法建立单链表,将新节点插入到链表的表尾
bool ListTailInsert(Node*& LHead)
{
	//定义一个尾指针,指向链表的结尾
	Node* rear = LHead;
	
	//读入要插入的节点以9999结束
	int i = 0;
	int nums[] = { 1,2,3,4,5,6,9999 };
	int num = nums[i++];
	while (num != 9999)
	{
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->Data = num;
		rear->next = newNode;
		rear = newNode;
		num = nums[i++];
	}
	rear->next = NULL;
	return true;
}

按序号查找节点值
//3.按序号查找节点值:找到第i个节点
Node* GetElem(Node* LHead, int index)
{
	if (LHead == NULL||index<0)
		return NULL;
	if (LHead->next == NULL||index==0)
		return LHead;
	Node* tempNode = LHead;//记录当前遍历到的节点
	int nodeIndex = 0;		//记录当前遍历的节点的序号
	while (tempNode&&nodeIndex < index)
	{
		tempNode = tempNode->next;
		nodeIndex++;
	}
	return tempNode;
}
按值查找节点的序号
//4.按值查找节点,找到值为value的节点,返回节点的位置
int LocateElem(Node* LHead, ElementType value)
{	
	Node* tempNode = LHead;//记录当前遍历到的节点
	int nodeIndex = 0;		//记录当前遍历的节点的序号
	while (tempNode&& tempNode->Data!=value )
	{
		tempNode = tempNode->next;
		nodeIndex++;
	}
	if (nodeIndex == GetListLen(LHead))
		return -1;
	return nodeIndex;
}

插入节点
//5.插入节点操作:将值为value的节点插入到第index个位置上
bool NodeInsert(Node*& LHead, ElementType value, int index)
{
	//判断插入的位置是否合理
	if (index < 1 || NULL == GetElem(LHead, index))
		return false;
	//先找到第index-1个位置
	Node* preNode = GetElem(LHead, index - 1);
	//创建新节点
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->Data = value;
	//插入
	newNode->next = preNode->next;
	preNode->next = newNode;
	return true;
}
删除节点
//6.删除节点操作:删除第i个节点
bool NodeDelete(Node*& LHead, int index)
{
	//判断删除的位置是否合理
	if (index < 1 || NULL == GetElem(LHead, index))
		return false;
	//先找到第index-1个位置
	Node* preNode = GetElem(LHead, index - 1);
	//记录第index个节点
	Node*p=preNode->next;
	//删除节点:令第index-1个节点的next指针指向第index+1个节点
	preNode->next = preNode->next->next;
	//释放第index个节点
	free(p);
	p = NULL;//防止出现野指针
	return true;
}

单链表的实现
/*单链表的实现*/
#include <stdio.h>
#include<stdlib.h>
typedef int ElementType ;

//定义单链表的节点
typedef struct Node {
	ElementType Data;//数据域
	struct Node* next;//指针域
}Node;

//1.头插法建立单链表,传入头指针L,把节点依次插入到都头结点之后
bool ListHeadInsert(Node*& LHead);
//2.尾插法建立单链表,将新节点插入到链表的表尾
bool ListTailInsert(Node*& LHead);
//3.按序号查找节点值:找到第i个节点,返回指向这个节点的指针
Node* GetElem(Node* LHead, int index);
//4.按值查找节点,找到值为value的节点,返回节点的位置
int LocateElem(Node* LHead, ElementType value);
//5.插入节点操作:将值为value的节点插入到第index个位置上
bool NodeInsert(Node*& LHead, ElementType value,int index);
//6.删除节点操作:删除第i个节点
bool NodeDelete(Node*& LHead, int index);
//7.求表长操作:求表的长度
int GetListLen(Node* LHead);
//8.打印链表
void PrintList(Node* LHead);
//9.释放链表
void DestroyList(Node*& LHead);

int main()
{
	//定义头指针head,head指向头节点
	Node* head = (Node*)malloc(sizeof(Node));
	head->next = NULL;
	head->Data = 0;	
	//头插法建立链表
	ListHeadInsert(head);
	PrintList(head);
	DestroyList(head);
	PrintList(head);

	//尾插法建立链表
	ListTailInsert(head);
	PrintList(head);

	//返回第3个节点的值
	if (GetElem(head, 3))
	{
		int value = GetElem(head, 3)->Data;
		printf("第三个节点的值为%d\n", value);
		//查找值为value的节点的坐标
		int index = LocateElem(head, value);
		printf("值为%d的节点的坐标为%d\n", value, index);
	}

	//在第2个位置插入666
	NodeInsert(head, 666, 2);
	PrintList(head);

	//删除插入的节点
	NodeDelete(head, 2);
	PrintList(head);

	//获得表长
	printf("链表的长度为%d\n", GetListLen(head));

}

//1.头插法建立单链表,传入头指针L,把节点依次插入到都头结点之后
bool ListHeadInsert(Node*& LHead)
{
	//读入要插入的节点以9999结束
	int i = 0;
	int nums[] = { 1,2,3,4,5,6,9999 };
	int num = nums[i++];
		
	while (num != 9999)
	{
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->Data = num;
		newNode->next = LHead->next;
		LHead->next = newNode;

		num = nums[i++];
	}	
	return true;
}

//2.尾插法建立单链表,将新节点插入到链表的表尾
bool ListTailInsert(Node*& LHead)
{
	//定义一个尾指针,指向链表的结尾
	Node* rear = LHead;
	
	//读入要插入的节点以9999结束
	int i = 0;
	int nums[] = { 1,2,3,4,5,6,9999 };
	int num = nums[i++];
	while (num != 9999)
	{
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->Data = num;
		rear->next = newNode;
		rear = newNode;
		num = nums[i++];
	}
	rear->next = NULL;
	return true;
}

//3.按序号查找节点值:找到第i个节点
Node* GetElem(Node* LHead, int index)
{
	if (LHead == NULL||index<0)
		return NULL;
	if (LHead->next == NULL||index==0)
		return LHead;
	Node* tempNode = LHead;//记录当前遍历到的节点
	int nodeIndex = 0;		//记录当前遍历的节点的序号
	while (tempNode&&nodeIndex < index)
	{
		tempNode = tempNode->next;
		nodeIndex++;
	}
	return tempNode;
}

//4.按值查找节点,找到值为value的节点,返回节点的位置
int LocateElem(Node* LHead, ElementType value)
{	
	Node* tempNode = LHead;//记录当前遍历到的节点
	int nodeIndex = 0;		//记录当前遍历的节点的序号
	while (tempNode&& tempNode->Data!=value )
	{
		tempNode = tempNode->next;
		nodeIndex++;
	}
	if (nodeIndex == GetListLen(LHead))
		return -1;
	return nodeIndex;
}

//5.插入节点操作:将值为value的节点插入到第index个位置上
bool NodeInsert(Node*& LHead, ElementType value, int index)
{
	//判断插入的位置是否合理
	if (index < 1 || NULL == GetElem(LHead, index))
		return false;
	//先找到第index-1个位置
	Node* preNode = GetElem(LHead, index - 1);
	//创建新节点
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->Data = value;
	//插入
	newNode->next = preNode->next;
	preNode->next = newNode;
	return true;
}

//6.删除节点操作:删除第i个节点
bool NodeDelete(Node*& LHead, int index)
{
	//判断删除的位置是否合理
	if (index < 1 || NULL == GetElem(LHead, index))
		return false;
	//先找到第index-1个位置
	Node* preNode = GetElem(LHead, index - 1);
	//记录第index个节点
	Node*p=preNode->next;
	//删除节点:令第index-1个节点的next指针指向第index+1个节点
	preNode->next = preNode->next->next;
	//释放第index个节点
	free(p);
	p = NULL;//防止出现野指针
	return true;
}

//7.求表长操作:求表的长度
int GetListLen(Node* LHead)
{
	Node* tempNode = LHead;//记录当前遍历到的节点
	int len = 0;		//记录当前遍历的节点的序号
	while (tempNode)
	{
		tempNode = tempNode->next;
		len++;
	}
	//去掉头节点的长度
	return len-1;
}

//8.打印链表
void PrintList(Node* LHead)
{
	printf("\nlist:\n");
	if (LHead == NULL || LHead->next == NULL)
		return;
	//从头节点之后的一个节点开始打印
	
	Node* tempNode = LHead->next;
	while (tempNode != NULL)
	{
		printf("%3d ", tempNode->Data);
		tempNode = tempNode->next;
	}
	printf("\n");
}

//9.释放链表,不释放头节点
void DestroyList(Node*& LHead)
{
	if (LHead->next == NULL)
		return;
	
	Node* p = LHead->next;
	Node* q = p->next;
	while (q != NULL)
	{
		free(p);
		p = q;
		q = q->next;
	}
	free(p);
	LHead->next = NULL;
}

2.3.2双链表

双链表既有前驱节点也有后继节点,插入和删除与单链表有较大差异,其他操作相同,以下为双链表的实现

#include <stdio.h>
#include<stdlib.h>
typedef int ElementType;

//定义双向链表的节点
typedef struct DoublyNode {
	ElementType Data;//数据域
	struct DoublyNode* prior;//前驱指针
	struct DoublyNode* next;//后继指针
}Node;

//1.头插法建立单链表,传入头指针L,把节点依次插入到都头结点之后
bool ListHeadInsert(Node*& LHead);
//2.尾插法建立单链表,将新节点插入到链表的表尾
bool ListTailInsert(Node*& LHead);
//3.按序号查找节点值:找到第i个节点,返回指向这个节点的指针
Node* GetElem(Node* LHead, int index);
//4.按值查找节点,找到值为value的节点,返回节点的位置
int LocateElem(Node* LHead, ElementType value);
//5.插入节点操作:将值为value的节点插入到第index个位置上
bool NodeInsert(Node*& LHead, ElementType value, int index);
//6.删除节点操作:删除第i个节点
bool NodeDelete(Node*& LHead, int index);
//7.求表长操作:求表的长度
int GetListLen(Node* LHead);
//8.打印链表
void PrintList(Node* LHead);
//9.释放链表
void DestroyList(Node*& LHead);

int main()
{
	//定义头指针head,head指向头节点
	Node* head = (Node*)malloc(sizeof(Node));
	head->Data = 0;
	head->next = NULL;
	head->prior = NULL;
	//头插法插入节点
	ListHeadInsert(head);	
	PrintList(head);//打印链表
	DestroyList(head);
	//尾插法插入节点
	ListTailInsert(head);
	PrintList(head);//打印链表
	//找第3个节点
	if (GetElem(head, 3))
	{
		printf("第三个节点为%d\n", GetElem(head, 3)->Data);
	}
	//找值为value的节点
	int value = 66;
	//int value = 6;
	if (LocateElem(head, value)!=-1)
	{
		printf("值为%d的节点在第%d个位置\n", value,LocateElem(head, value));
	}
	else {
		printf("值为6的节点不存在\n");
	}
	//在第二个位置插入88
	printf("在第二个位置插入88之后的链表\n");
	NodeInsert(head, 88, 2);
	PrintList(head);

	//删除第6个节点
	printf("删除第6个节点之后的链表\n");
	NodeDelete(head, 6);
	PrintList(head);
	return 0;

}


//1.头插法建立单链表,传入头指针L,把节点依次插入到都头结点之后
bool ListHeadInsert(Node*& LHead)
{
	//定义插入的数据,以9999结束
	ElementType values[10] = { 1,2,3,4,5,66,7,8,9,9999 };
	int i = 0;
	ElementType NodeValue = values[i++];
	while (NodeValue != 9999)
	{
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->Data = NodeValue;
		newNode->next = LHead->next;
		//如果不是第一个节点的话就把头节点后一个节点的前驱指针的指向改为新节点
		if (LHead->next != NULL)
		{
			LHead->next->prior = newNode;
		}
		newNode->prior = LHead;
		LHead->next = newNode;
		NodeValue = values[i++];
	}
	return true;
}
//2.尾插法建立单链表,将新节点插入到链表的表尾
bool ListTailInsert(Node*& LHead)
{
	ElementType values[10] = { 1,2,3,4,5,66,7,8,9,9999 };
	int i = 0;
	ElementType NodeValue = values[i++];
	Node* rear = LHead;//定义尾指针
	while (NodeValue != 9999)
	{
		Node* newNode = (Node*)malloc(sizeof(Node));
		newNode->Data = NodeValue;
		rear->next = newNode;
		newNode->prior = rear;
		rear = newNode;
		NodeValue = values[i++];
	}
	//最后一个节点的next指针指向null
	rear->next = NULL;
	return true;
}
//3.按序号查找节点值:找到第i个节点,返回指向这个节点的指针
Node* GetElem(Node* LHead, int index)
{
	int j = 1;
	Node* temp = LHead->next;
	if (index == 0)
		return LHead;
	if (index < 1)
		return NULL;
	while (temp && j < index)
	{
		temp = temp->next;
		j++;
	}
	return temp;
}
//4.按值查找节点,找到值为value的节点,返回节点的位置
int LocateElem(Node* LHead, ElementType value)
{
	int j = 1;
	Node* temp = LHead->next;
	
	while (temp && temp->Data!=value)
	{
		temp = temp->next;
		j++;
	}
	if (j >= GetListLen(LHead))
		return -1;
	return j;
}
//5.插入节点操作:将值为value的节点插入到第index个位置上
bool NodeInsert(Node*& LHead, ElementType value, int index)
{
	//判断插入的位置是否合理
	if (index < 1 || NULL == GetElem(LHead, index))
		return false;
	//先找到第index-1个位置
	Node* preNode = GetElem(LHead, index - 1);
	//创建新节点
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->Data = value;
	//插入
	newNode->next = preNode->next;
	if (preNode->next)
	{
		preNode->next->prior = newNode;
	}
	newNode->prior = preNode;
	preNode->next = newNode;

	return true;
}

//6.删除节点操作:删除第i个节点
bool NodeDelete(Node*& LHead, int index)
{
	
	if (index < 1 || NULL == GetElem(LHead, index))
		return false;
	//先找到第index-1个位置
	Node* preNode = GetElem(LHead, index - 1);
	//记录第index个节点
	Node* p = preNode->next;

	//删除节点:令第index-1个节点的next指针指向第index+1个节点
	preNode->next = p->next;
	//p的后一个节点不为空,要改这个节点的前驱指针
	if (p->next)
		p->next->prior = p->prior;

	//释放第index个节点
	free(p);
	p = NULL;//防止出现野指针
	return true;
}

//7.求表长操作:求表的长度
int GetListLen(Node* LHead)
{
	Node* tempNode = LHead;//记录当前遍历到的节点
	int len = 0;		//记录当前遍历的节点的序号
	while (tempNode)
	{
		tempNode = tempNode->next;
		len++;
	}
	//去掉头节点的长度
	return len - 1;
}
//8.打印链表
void PrintList(Node* LHead)
{
	Node* TempNode = LHead->next;
	while (TempNode)
	{
		printf("%4d", TempNode->Data);
		TempNode = TempNode->next;
	}
	printf("\n");
}
//9.释放链表
void DestroyList(Node*& LHead)
{
	if (LHead->next == NULL)
		return;

	Node* p = LHead->next;
	Node* q = p->next;
	while (q != NULL)
	{
		free(p);
		p = q;
		q = q->next;
	}
	free(p);
	LHead->next = NULL;
}

2.3.3循环双链表

循环双链表只需要在双链表的基础上,让头节点的前驱指针指向表尾节点,尾部节点的后继指针指向头节点即可

2.3.4静态链表

静态链表是借助数组来存线性表的链式结构,节点有数据域也有指针域,但是指针域是下一个节点在当前数组中的位置

下标数据域指针域
02
1b6
2a1
3d-1
4
5
6c3

头节点->a->b->c->d

//定义静态链表的节点
typedef struct{
	int data;
    int next;
}int list[10];

2.4 顺序表和链表的比较

1.读写方式

顺序表可以顺序存取也可以随机存取

链表只能顺序存取

2.结构

顺序表和链表在逻辑结构上都是相邻的

但是在物理结构中,顺序表是相邻的,链表不是,对应的逻辑关系通过指针链接来表示

3.查找插入删除操作

对于按序号查找,顺序表查找复杂度为O(1),但是链表复杂度为O(n)

对于插入删除,链表只需要改指针域,但是顺序表需要平均移动半个数组

4.空间分配

顺序表需要预先分配空间,扩充的成本较高,需要大量移动元素,且静态数组没法扩充,链表扩充成本很低,只要空间足够就能扩充。

一句话,难以估计存储规模用链表,访问较多用顺序表,插入删除较多用链表,实现简单用顺序表。

2.5典型例题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值