c语言建立线性表(顺序储存,链式储存,循环,双向)全

顺序储存

储存结构

采用的是用内存动态分配方式定义线性表的顺序储存结构

#define LIST_MAX_SIZE 100  //线性表的最大长度
#define LISTINCREMENT 10  //线性表一次增加的量

typedef struct {
	int* elem; //指向存放线性表数据元素的基地址
	int length; //线性表当前长度(当前存储的元素个数)
	int listsize; // 当前分配的存储容量
}SQList;

初始化(建立)顺序表

操作步骤:

  1. 申请一片连续的空间,并将其地址空间赋给elem指针变量。
  2. 开始是length 值为0.
  3. 对listsize 赋初值,其值是申请空间的最大容量。
//创建一个线性表 
//注意点是:如果无法分配空间,打印提示,并直接返回
void CreateList(SQList &L)
{
	L.elem = (int *)malloc(LIST_MAX_SIZE * sizeof(int));
	if (!L.elem) {
		printf("分配地址出错\n");
		return;
	}
	L.length = 0;
	L.listsize = LIST_MAX_SIZE;
}

//求线性表中的元素个数
int ListLength(SQList L) {
	return L.length;
}

查找操作

一、按值查找,找到返回对应的下标

一、按值查找,找到返回对应的下标,没有则返回-1。如果有多个返回第一个的位置
条件:1、线性表存在;2、线性表中有元素

算法:在条件满足的情况下,遍历一遍,如果下标在i~length之间就返回下标

int ListLocate(SQList L, int e) {
	if (!L.elem ) {
		printf("线性表没有初始化\n");
		return -1;
	}
	if (L.length == 0) {
		printf("线性表中没有元素\n");
		return -1;
	}
	int i = 1;
	//下标是从1开始存储元素的
	while (i <= L.length && e != L.elem[i])
		i++;
	if (i <= L.length) {
		printf("元素已经找到\n");
		return i;
	}
	else {
		printf("线性表中没有该元素\n");
		return -1;
	}
}

二、按照下标返回元素

条件:1、线性表存在;2、下表没有越界

int GetList(SQList L,int i) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return -1;
	}
	if (i<1 || i>L.length) {
		printf("数组下标越界\n");
		return -1;
	}
	return L.elem[i];
}

插入操作

插入步骤,在后边一定要将 length 加1

一、在线性表尾部添加元素

条件:线性表存在

在条件满足情况下:L.elem[++L.length] = val; 表示在尾部添加元素

void ListPush(SQList& L,int val) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return ;
	}
	if (L.length == L.listsize)//当前存储空间已经满了,增加分量
	{
		//在原来基础上扩大
		int* newbase = (int*)realloc(L.elem, (L.listsize + LISTINCREMENT) * sizeof(int));
		if (!newbase) {
			printf("分配空间错误\n");
			return;
		}
		L.elem = newbase; //获得新基址
		L.listsize += LISTINCREMENT;//更新容量
	}
	//在尾部插入
	L.elem[++L.length] = val;
}

二、在位置i处插入元素

步骤:

  1. 将位置i以后的元素后移一个位置。
  2. 在i处插入值
void ListInsert(SQList& L, int i, int val) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return;
	}
	if (L.length == L.listsize)//当前存储空间已经满了,增加分量
	{
		//在原来基础上扩大
		int* newbase = (int*)realloc(L.elem, (L.listsize + LISTINCREMENT) * sizeof(int));
		if (!newbase) {
			printf("分配空间错误\n");
			return;
		}
		L.elem = newbase; //获得新基址
		L.listsize += LISTINCREMENT;//更新容量
	}
	for (int j = L.length; j >= i; --j) {
		L.elem[j + 1] = L.elem[j]; //i后面的元素后移
	}
	//插入元素
	L.elem[i] = val;
	L.length++;
}

三、顺序表(有序)插入,(如都是由小到大)

基本思路是:

  1. 先找到值val插入的位置。

int pos = L.length;
	while (pos > 0 && val < L.elem[pos])
		pos--;
	//这个循坏结束后,POS对应位置元素是小于等于val的,所以可以将val插到其后边
for (int i = L.length; i >= pos+1; i--)
		L.elem[i + 1] = L.elem[i];
	//在pos后边插入
	L.elem[pos + 1] = val; 
  1. 然后将该位置以后到结尾的元素依次向后移到一位,然后在空出的位置插入val

void ListInsertorder(SQList& L, int val) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return;
	}
	if (L.length == L.listsize)//当前存储空间已经满了,增加分量
	{
		//在原来基础上扩大
		int* newbase = (int*)realloc(L.elem, (L.listsize + LISTINCREMENT) * sizeof(int));
		if (!newbase) {
			printf("分配空间错误\n");
			return;
		}
		L.elem = newbase; //获得新基址
		L.listsize += LISTINCREMENT;//更新容量
	}
	//找到要插入的位置
	int pos = L.length;
	while (pos > 0 && val < L.elem[pos])
		pos--;
	//这个循坏结束后,POS对应位置元素是小于等于val的,所以可以将val插到其后边
	for (int i = L.length; i >= pos+1; i--)
		L.elem[i + 1] = L.elem[i];
	//插入
	L.elem[pos + 1] = val;
	L.length++;
}

删除操作

插入步骤,在后边一定要将 length 减1

一、删除位置i的元素,删除成功后,返回删除的值

两种情况一个是在尾部,另一个是其余位置。 如果是在结尾只要length减一就可以了。
条件:添加 i不能越界

基本步骤:

  1. 判断位置i是否合理
  2. 将位置i处的值取出
  3. 将位置i+1到结尾的元素向前移动一位
  4. length –
int ListDelete(SQList& L, int i) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return -1;
	}
	if (i<1 || i>L.length) {
		printf("数组越界\n");
		return -1;
	}
	int val = L.elem[i];
	if (i == L.length)
		L.length--;
	else
	{
		//元素往前移
		for (; i < L.length; i++)
			L.elem[i] = L.elem[i + 1];
		L.length--;
	}
	return val;
}

二、删除值为val的第一个元素,没有返回-1

int ListDelete_Sq(SQList& L, int val) {
	//找到元素在的位置
	int i = 1;
	while (i <= L.length && val != L.elem[i])
		i++;
	//分情况删除
	if (i == L.length)
		L.length--;
	else if (i < L.length) {
		//元素前移
		for (; i < L.length; ++i)
			L.elem[i] = L.elem[i + 1];
		L.length--;
	}
	else
	{
		printf("要删除的数据元素不存在\n");
		return -1;
	}
}

三、在非递减有序的有序表中删除多余的相同元素

基本思路是:

因为是有序的所有相同元素一定是相邻的,所有只要依次比较相邻的元素,删去相同的。

void Listdelete_Sq(SQList& L) {
	int i = 1;
	while (i < L.length) {
		if (L.elem[i] != L.elem[i + 1])i++;
		else { //删除 第i+1个元素
			if (i == L.length - 1)
				L.length--;
			else {
				for (int j = i + 1; j < L.length; j++)
					L.elem[j] = L.elem[j + 1];//删除第i个元素
				L.length--;//长度减一
			}
		}
	}
}

其余操作

一、将线性表中的所有元素转置

void reverse_Sq(SQList& L) {
	int i = 1, j = L.length;
	while (i < j)
	{
		int temp = L.elem[i];
		L.elem[i] = L.elem[j];
		L.elem[j] = temp;
		i++, j--;
	}
}

二、两个有序的顺序表合并后任然有序

思路是:

取出第一个边和第二个表中最小的数据,两者比较,将最小的插入到第三个表中,重复上述步骤。直到有一个表为空。然后继续将不为空的表中的元素依次插入到第三个表中。

SQList MergeList_Sq(SQList La, SQList Lb) {
	SQList Lc;
	Lc.listsize = Lc.length = La.length + Lb.length;
	Lc.elem = (int*)malloc(Lc.listsize * sizeof(int));
	int i, j, k;
	i = j = k = 1;
	while (i <= La.length && j <= Lb.length) {
		if (La.elem[i] <= Lb.elem[j])Lc.elem[k++] = La.elem[i++];
		else
			Lc.elem[k++] = Lb.elem[j++];
	}
	while(i<=La.length)
		Lc.elem[k++] = La.elem[i++];
	while(j<=Lb.length)
		Lc.elem[k++] = Lb.elem[j++];
	return Lc;
}

完整代码

#include<stdio.h>
#include<malloc.h>

#define LIST_MAX_SIZE 100  //线性表的最大长度
#define LISTINCREMENT 10  //线性表一次增加的量

typedef struct {
	int* elem; //指向存放线性表数据元素的基地址
	int length; //线性表当前长度(当前存储的元素个数)
	int listsize; // 当前分配的存储容量
}SQList;

//创建一个线性表 
//注意点是:如果无法分配空间,打印提示,并直接返回
void CreateList(SQList &L)
{
	L.elem = (int *)malloc(LIST_MAX_SIZE * sizeof(int));
	if (!L.elem) {
		printf("分配地址出错\n");
		return;
	}
	L.length = 0;
	L.listsize = LIST_MAX_SIZE;
}

//求线性表中的元素个数
int ListLength(SQList L) {
	return L.length;
}

//增加容量 
void increte_List(SQList& L) {
	//在原来基础上扩大
	int* newbase = (int*)realloc(L.elem, (L.listsize + LISTINCREMENT) * sizeof(int));
	if (!newbase) {
		printf("分配空间错误\n");
		return;
	}
	L.elem = newbase; //获得新基址
	L.listsize += LISTINCREMENT;//更新容量
}

/* 查找操作*/
//一、按值查找,找到返回对应的下标,没有则返回-1。如果有多个返回第一个的位置
//条件:1、线性表存在;2、线性表中有元素

int ListLocate(SQList L, int e) {
	if (!L.elem ) {
		printf("线性表没有初始化\n");
		return -1;
	}
	if (L.length == 0) {
		printf("线性表中没有元素\n");
		return -1;
	}
	int i = 1;
	//下标是从1开始存储元素的
	while (i <= L.length && e != L.elem[i])
		i++;
	if (i <= L.length) {
		printf("元素已经找到\n");
		return i;
	}
	else {
		printf("线性表中没有该元素\n");
		return -1;
	}
}

//二、按照下标返回元素
//条件:1、线性表存在;2、下表没有越界

int GetList(SQList L,int i) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return -1;
	}
	if (i<1 || i>L.length) {
		printf("数组下标越界\n");
		return -1;
	}
	return L.elem[i];
}

/* 插入元素*/
//一、在线性表尾部添加元素
//条件:线性表存在
void ListPush(SQList& L,int val) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return ;
	}
	if (L.length == L.listsize)//当前存储空间已经满了,增加分量
		increte_List(L);
	//在尾部插入
	L.elem[++L.length] = val;
}

//二、在位置i处插入元素
void ListInsert(SQList& L, int i, int val) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return;
	}
	if (L.length == L.listsize)//当前存储空间已经满了,增加分量
		increte_List(L);
	for (int j = L.length; j >= i; --j) {
		L.elem[j + 1] = L.elem[j]; //i后面的元素后移
	}
	//插入元素
	L.elem[i] = val;
	L.length++;
}

//三、顺序表(有序)插入,(如都是由小到大)
void ListInsertorder(SQList& L, int val) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return;
	}
	if (L.length == L.listsize)//当前存储空间已经满了,增加分量
		increte_List(L);
	//找到要插入的位置
	int pos = L.length;
	while (pos > 0 && val < L.elem[pos])
		pos--;
	//这个循坏结束后,POS对应位置元素是小于等于val的,所以可以将val插到其后边
	for (int i = L.length; i >= pos+1; i--)
		L.elem[i + 1] = L.elem[i];
	//插入
	L.elem[pos + 1] = val;
	L.length++;
}

/* 删除操作*/
// 一、删除位置i的元素,删除成功后,返回删除的值
// 两种情况一个是在尾部,另一个是其余位置
//添加 i不能越界
int ListDelete(SQList& L, int i) {
	if (!L.elem) {
		printf("线性表没有初始化\n");
		return -1;
	}
	if (i<1 || i>L.length) {
		printf("数组越界\n");
		return -1;
	}
	int val = L.elem[i];
	if (i == L.length)
		L.length--;
	else
	{
		//元素往前移
		for (; i < L.length; i++)
			L.elem[i] = L.elem[i + 1];
		L.length--;
	}
	return val;
}

//二、删除值为val的第一个元素,没有返回-1

int ListDelete_Sq(SQList& L, int val) {
	//找到元素在的位置
	int i = 1;
	while (i <= L.length && val != L.elem[i])
		i++;
	//分情况删除
	if (i == L.length)
		L.length--;
	else if (i < L.length) {
		//元素前移
		for (; i < L.length; ++i)
			L.elem[i] = L.elem[i + 1];
		L.length--;
	}
	else
	{
		printf("要删除的数据元素不存在\n");
		return -1;
	}
}

//三、在非递减有序的有序表中删除多余的相同元素
void Listdelete_Sq(SQList& L) {
	int i = 1;
	while (i < L.length) {
		if (L.elem[i] != L.elem[i + 1])i++;
		else { //删除 第i+1个元素
			if (i == L.length - 1)
				L.length--;
			else {
				for (int j = i + 1; j < L.length; j++)
					L.elem[j] = L.elem[j + 1];//删除第i个元素
				L.length--;//长度减一
			}
		}
	}
}

/*其余操作*/
//一、将线性表中的所有元素转置

void reverse_Sq(SQList& L) {
	int i = 1, j = L.length;
	while (i < j)
	{
		int temp = L.elem[i];
		L.elem[i] = L.elem[j];
		L.elem[j] = temp;
		i++, j--;
	}
}

//二、两个有序的顺序表合并后任然有序
SQList MergeList_Sq(SQList La, SQList Lb) {
	SQList Lc;
	Lc.listsize = Lc.length = La.length + Lb.length;
	Lc.elem = (int*)malloc(Lc.listsize * sizeof(int));
	int i, j, k;
	i = j = k = 1;
	while (i <= La.length && j <= Lb.length) {
		if (La.elem[i] <= Lb.elem[j])Lc.elem[k++] = La.elem[i++];
		else
			Lc.elem[k++] = Lb.elem[j++];
	}
	while(i<=La.length)
		Lc.elem[k++] = La.elem[i++];
	while(j<=Lb.length)
		Lc.elem[k++] = Lb.elem[j++];
	return Lc;
}

void show(SQList L)
{
	for (int i = 1; i <= L.length; ++i)
		printf("%d ", L.elem[i]);
	printf("\n");
}

int main()
{
	SQList La, Lb,Lc;
	CreateList(La);
	CreateList(Lb);
	for (int i = 1; i <= 5; ++i)
	{
		int x;
		scanf_s("%d", &x);
		ListPush(La, x);
	}
	for (int i = 1; i <= 5; ++i)
	{
		int x;
		scanf_s("%d", &x);
		ListPush(Lb, x);
	}
	show(La), show(Lb);
	reverse_Sq(Lb);
	show(Lb);
	Lc = MergeList_Sq(La, Lb);
	show(Lc);
	return 0;
}




链式储存

存储结构

typedef struct LNode {
	int date;
	LNode* next;
}LNode;

建立链表

一、尾插法建立

,需要建立三个指针,一个指向头指针,一个始终指向尾部,一个指向新建立的

基本思路是:

为头节点申请空间后,让s也指向头结点。然后为p申请空间并赋值,将s的下一个指针指向p,然后指针s后移。(即将p赋给s),重复这个步骤

LNode* create_E(int n) {
	LNode* head, * s,*p;
	head = (LNode *)malloc(sizeof(LNode));
	head->next = NULL;
	s = head;
	while (n--) {
		p = (LNode*)malloc(sizeof(LNode));
		scanf_s("%d", &p->date);
		s->next = p; //把新节点插入链表的结尾
		s = p; //指针s后移
	}
	s->next = NULL;
	return head;
}

二、头插法建立

核心思路:

在头结点后插入新节点。理解好这句。
我们要在头结点后边插入新节点,要什么做呢?首先是将原本头结点的next放到新建立的节点的next把,然后再讲头结点的next指向p。

//只需要两个指针
LNode* create_H(int n) {  //得到的链表与输入的值相反
	LNode* head, * p;
	head = (LNode*)malloc(sizeof(LNode));
	head->next = NULL;
	while (n--) {
		p = (LNode*)malloc(sizeof(LNode));
		scanf_s("%d", &p->date);
		p->next = head->next; //在头结点后插入新节点
		head->next = p;
	}
	return head;
}

查找位置i的两种方法

1、在i满足1<=i<=length 的情况下一直往后移动i次就可以了。

LNode* p = head->next;
	int len = GetLength(head);
	if (i > len||i<1) {
		printf("i值不合理\n");
		return -1;
	}
	while (--i) {
		p = p->next;
	}

因为在这里 p初值是 head->next 所以只要移动i-1次就到位置i了,所以是while(–i),如果是p初值是 head,则是 i–

2、

LNode* p, * s;
	p = head; //赋值是头结点,不是next
	int j = 0;
	while (p && j < i ) { //找第i个位置
		p = p->next;
		j++;
	}
	if (!p || j > i) //对应的情况是i超过长度,与i小于0
	{
		printf("i不合理\n");
		return;
	}

链表的查找操作

一、在链表中查找第i的节点

//需要检测 i的值是否合理

int GetElem(LNode* head,int i) {
	LNode* p = head->next;
	int len = GetLength(head);
	if (i > len||i<1) {
		printf("i值不合理\n");
		return -1;
	}
	while (--i) {
		p = p->next;
	}
	return p->date;
}

二、在单链表中按值查找第一个与val相等的节点

int LocateElem(LNode* head,int val) {
	int i = 1;
	LNode* p = head->next;
	if (!p)
	{
		printf("当前链表为空\n");
		return -1;
	}
	while (p && p->date != val) {
		p = p->next;
		i++;
	}
	//printf("位置i为:%d", i);
	if (i > GetLength(head))
	{
		printf("当前链表中没有该值\n");
		return -1;
	}
	else
		return i;

}

三、查找与val值相等的节点个数

int findnum(LNode* head,int val) {
	LNode* p = head->next;
	int num = 0;
	while (p) {
		if (p->date == val)
			num++;
		p = p->next;
	}
	return num;
}

四、找链表中的最大值与最小值


void find_max_min(LNode* head) {
	LNode* p = head->next;
	int Max = p->date, Min = p->date;
	while (p) {
		if (p->date > Max)
			Max = p->date;
		else
			Min = p->date;
		p = p->next;
	}
	printf("最大值是:%d  最小值是:%d\n", Max, Min);
}

插入操作

一、在位置i前插入数据val

void ListInsert_P(LNode* head, int i, int val) {
	LNode* p, * s;
	p = head; //赋值是头结点,不是next
	int j = 0;
	while (p && j < i - 1) { //找第i-1个位置
		p = p->next;
		j++;
	}
	if (!p || j > i - 1) //对应的情况是i超过长度,与i小于1
		return;
	else
	{
		s = (LNode*)malloc(sizeof(LNode));
		s->date = val;
		s->next = p->next;
		p->next = s;
	}
}

二、在位置i后插入数据val

//第0个就是在头结点后边
void ListInsert_N(LNode* head, int i, int val) {
	LNode* p, * s;
	p = head; //赋值是头结点,不是next
	int j = 0;
	while (p && j < i ) { //找第i个位置
		p = p->next;
		j++;
	}
	//printf("j: %d\n", j);
	if (!p || j > i) //对应的情况是i超过长度,与i小于0
	{
		printf("i不合理\n");
		return;
	}
	else
	{
		s = (LNode*)malloc(sizeof(LNode));
		s->date = val;
		s->next = p->next;
		p->next = s;
	}
}

三、在第一个值为val的后边添加值val1

void ListInsert_V(LNode* head, int val, int val1) {
	int i = LocateElem(head, val);//找到值val对应的位置
	//printf("位置i为:%d", i);
	ListInsert_N(head, i, val1);
}

删除操作

int ListDelete(LNode* head, int i) {
	LNode* p, * q;
	int val;
	p = head;
	int j = 0;
	while (p->next && j > i - 1) {
		p = p->next;
		j++;
	}
	if (!p->next || j > i - 1)
		return -1;
	else
	{
		q = p->next;
		p->next = q->next;
		val = q->date;
		free(q);//释放删除的节点
	}
	return val;
}

链表逆置

void List_reverse(LNode* head) {
	LNode* p, * pre,*temp;
	pre = NULL;
	p = head->next;
	while (p) {
		temp = p->next;
		p->next = pre;
		pre = p;
		p = temp;
	}
	head->next = pre; //pre指向的是原来链表的终点
}

合并两个有序表

LNode* MargeList(LNode* head1, LNode* head2) {
	LNode* Lc,*La,*Lb,*head;
	head = (LNode*)malloc(sizeof(LNode));
	head->next = NULL;
	Lc = head;
	La = head1->next;
	Lb = head2->next;
	while (La && Lb) {
		if (La->date <= Lb->date)
		{
			Lc->next = La;
			Lc = La;  //记得后移
			La = La->next;

		}
		else
		{
			Lc->next = Lb;
			Lc = Lb;
			Lb = Lb->next;
		}
	}
	Lc->next = La ? La : Lb;
	return head;
}

完整代码

#include<stdio.h>
#include<malloc.h>


typedef struct LNode {
	int date;
	LNode* next;
}LNode;

/* 建立链表*/
//一、尾插法建立,需要建立三个指针
//一个指向头指针,一个始终指向尾部,一个指向新建立的
LNode* create_E(int n) {
	LNode* head, * s,*p;
	head = (LNode *)malloc(sizeof(LNode));
	head->next = NULL;
	s = head;
	while (n--) {
		p = (LNode*)malloc(sizeof(LNode));
		scanf_s("%d", &p->date);
		s->next = p; //把新节点插入链表的结尾
		s = p; //指针s后移
	}
	s->next = NULL;
	return head;
}

//二、头插法建立 
//只需要两个指针
LNode* create_H(int n) {  //得到的链表与输入的值相反
	LNode* head, * p;
	head = (LNode*)malloc(sizeof(LNode));
	head->next = NULL;
	while (n--) {
		p = (LNode*)malloc(sizeof(LNode));
		scanf_s("%d", &p->date);
		p->next = head->next; //在头结点后插入新节点
		head->next = p;
	}
	return head;
}

/*求链表的长度*/
int GetLength(LNode* head) {
	LNode* p = head->next;
	int len = 0;
	while (p) {
		p = p->next;
		len++;
	}
	return len;
}


/*链表的查找*/
//一、在链表中查找第i的节点
//需要检测 i的值是否合理

int GetElem(LNode* head,int i) {
	LNode* p = head->next;
	int len = GetLength(head);
	if (i > len||i<1) {
		printf("i值不合理\n");
		return -1;
	}
	while (--i) {
		p = p->next;
	}
	return p->date;
}

//二、在单链表中按值查找第一个与val相等的节点
int LocateElem(LNode* head,int val) {
	int i = 1;
	LNode* p = head->next;
	if (!p)
	{
		printf("当前链表为空\n");
		return -1;
	}
	while (p && p->date != val) {
		p = p->next;
		i++;
	}
	//printf("位置i为:%d", i);
	if (i > GetLength(head))
	{
		printf("当前链表中没有该值\n");
		return -1;
	}
	else
		return i;

}
// 三、查找与val值相等的节点个数
int findnum(LNode* head,int val) {
	LNode* p = head->next;
	int num = 0;
	while (p) {
		if (p->date == val)
			num++;
		p = p->next;
	}
	return num;
}

//四、找链表中的最大值与最小值
void find_max_min(LNode* head) {
	LNode* p = head->next;
	int Max = p->date, Min = p->date;
	while (p) {
		if (p->date > Max)
			Max = p->date;
		else
			Min = p->date;
		p = p->next;
	}
	printf("最大值是:%d  最小值是:%d\n", Max, Min);
}

/*插入操作*/
//一、在位置i前插入数据val

void ListInsert_P(LNode* head, int i, int val) {
	LNode* p, * s;
	p = head; //赋值是头结点,不是next
	int j = 0;
	while (p && j < i - 1) { //找第i-1个位置
		p = p->next;
		j++;
	}
	if (!p || j > i - 1) //对应的情况是i超过长度,与i小于1
		return;
	else
	{
		s = (LNode*)malloc(sizeof(LNode));
		s->date = val;
		s->next = p->next;
		p->next = s;
	}
}

//二、在位置i后插入数据val
//第0个就是在头结点后边
void ListInsert_N(LNode* head, int i, int val) {
	LNode* p, * s;
	p = head; //赋值是头结点,不是next
	int j = 0;
	while (p && j < i ) { //找第i个位置
		p = p->next;
		j++;
	}
	//printf("j: %d\n", j);
	if (!p || j > i) //对应的情况是i超过长度,与i小于0
	{
		printf("i不合理\n");
		return;
	}
	else
	{
		s = (LNode*)malloc(sizeof(LNode));
		s->date = val;
		s->next = p->next;
		p->next = s;
	}
}

//三、在第一个值为val的后边添加值val1
void ListInsert_V(LNode* head, int val, int val1) {
	int i = LocateElem(head, val);//找到值val对应的位置
	//printf("位置i为:%d", i);
	ListInsert_N(head, i, val1);
}

/*删除操作*/
//一、删除位置i的节点,并返回删除节点的值

int ListDelete(LNode* head, int i) {
	LNode* p, * q;
	int val;
	p = head;
	int j = 0;
	while (p->next && j > i - 1) {
		p = p->next;
		j++;
	}
	if (!p->next || j > i - 1)
		return -1;
	else
	{
		q = p->next;
		p->next = q->next;
		val = q->date;
		free(q);//释放删除的节点
	}
	return val;
}

/*链表逆置*/
void List_reverse(LNode* head) {
	LNode* p, * pre,*temp;
	pre = NULL;
	p = head->next;
	while (p) {
		temp = p->next;
		p->next = pre;
		pre = p;
		p = temp;
	}
	head->next = pre; //pre指向的是原来链表的终点
}

/*合并两个有序表*/
LNode* MargeList(LNode* head1, LNode* head2) {
	LNode* Lc,*La,*Lb,*head;
	head = (LNode*)malloc(sizeof(LNode));
	head->next = NULL;
	Lc = head;
	La = head1->next;
	Lb = head2->next;
	while (La && Lb) {
		if (La->date <= Lb->date)
		{
			Lc->next = La;
			Lc = La;  //记得后移
			La = La->next;

		}
		else
		{
			Lc->next = Lb;
			Lc = Lb;
			Lb = Lb->next;
		}
	}
	Lc->next = La ? La : Lb;
	return head;
}

void show(LNode* head) {
	LNode* p = head->next;
	while (p) {
		printf("%d ", p->date);
		p = p->next;
	}
	printf("\n");
}

int main() {
	LNode* head1,*head2,*head;
	head1 = create_E(5);
	show(head1);
	/*head2 = create_H(5);
	show(head2);
	List_reverse(head2);
	show(head2);
	head = MargeList(head1, head2);
	show(head);*/
	ListInsert_V(head1, 3, 8);
	show(head1);
	ListInsert_P(head1, 3, 47);
	show(head1);
	return 0;
}

循环链表

  • 简单来说就是将原本最后一个结点的指针域由空指针指向头结点。
  • 最后一个结点的语句是:p->next == head
/* 创建循环链表(返回尾指针)*/

LNode* circular(int n) {
	LNode* head, * s, * p,*tail;
	head = (LNode*)malloc(sizeof(LNode));//为头结点申请空间
	head->next = NULL;
	s = head;
	while (n--) {
		p = (LNode*)malloc(sizeof(LNode));
		scanf_s("%d", &p->date);
		s->next = p; //把新节点插入链表的结尾
		s = p; //指针s后移
	}
	s->next = head;//建立循环
	tail = s;
	return tail;
}

/*打印循环链表*/
void show_cir(LNode* tail) {
	LNode* head = tail->next;
	LNode* p = tail->next->next;
	while (p != head) {
		printf("%d ", p->date);
		p = p->next;
	}
	printf("\n");
}

双向链表

储存结构

/*双向链表*/
typedef struct Node {
	int date;
	Node* prior;
	Node* next;
}DuLinkList;

建立双向链表

/*建立双向链表*/
DuLinkList* create_DuL(int n) {
	DuLinkList* head, * p, * s;
	head = (DuLinkList*)malloc(sizeof(DuLinkList));
	head->next = NULL;
	head->prior = NULL;
	s = head;
	while (n--) {
		p = (DuLinkList*)malloc(sizeof(DuLinkList));
		scanf_s("%d", &p->date);
		s->next = p;
		p->prior = s;
		s = p;
	}
	s->next = NULL;
	return head;
}

插入操作

/*双向链表的插入操作*/
void ListInsert_DuL(DuLinkList* head, int i, int val) {
	int len = Get_num(head);
	if (i<1 || i>len)
	{
		printf("i不合法\n");
		return;
	}
	DuLinkList* p ,*s;
	s = (DuLinkList*)malloc(sizeof(DuLinkList));
	s->date = val;
	p = head;
	while (--i)//i-- 得到的是位置i的结点,
		p = p->next;
	//printf("p = %d\n", p->date);
	//循环结束后p指向的是i的前一个结点
	s->next = p->next;
	p->next->prior = s;
	p->next = s;
	s->prior = p;
}

删除操作


/*双向链表的删除操作*/
int ListDelete_DuL(DuLinkList* head, int i) {
	int len = Get_num(head);
	if (i<1 || i>len)
	{
		printf("i不合法\n");
		return -1;
	}
	DuLinkList* p;
	p = head;
	while (--i)
		p = p->next;
	//printf("p = %d\n", p->date);
	//循环结束后p指向的是i的前一个结点
	p = p->next; //为了简便指向位置i的结点
	int val = p->date;
	p->prior->next = p->next;
	p->next->prior = p->prior;
	free(p);
	return val;
}

其余操作

/*双向链表中结点个数(存有数据的)*/
int Get_num(DuLinkList* head) {
	DuLinkList* p = head->next;
	int num = 0;
	while (p != NULL) {
		p = p->next;
		num++;
	}
	//printf("num = %d\n", num);
	return num;
}

void show_DuL(DuLinkList* head) {
	DuLinkList* p = head->next;
	while (p) {
		printf("%d ", p->date);
		p = p->next;
	}
	printf("\n");
}

在这里插入图片描述

  • 49
    点赞
  • 274
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
作 者:严蔚敏 李冬梅 吴伟民 出版时间:2011-2-1 0:00:00 [1] 字 数:398千字 责任编辑:蒋亮 页 数:236页 开 本:16 ISBN书号:978-7-115-23490-2 所属分类:本科 >> 计算机类 >> 数据结构与算法 所属丛书:21世纪高等学校计算机规划教材——名家系列 定 价:¥28.00元 第1章 绪论 1 1.1 数据结构的研究内容 1 1.2 基本概念和术语 3 1.2.1 数据、数据元素、数据项和数据对象 3 1.2.2 数据结构 4 1.2.3 数据类型和抽象数据类型 6 1.3 抽象数据类型的示与实现 7 1.4 算法和算法分析 11 1.4.1 算法的定义及特性 11 1.4.2 评价算法优劣的基本标准 11 1.4.3 算法的时间复杂度 12 1.4.4 算法的空间复杂度 14 1.5 小结 15 习题 16 第2章 线性 18 2.1 线性的类型定义 18 2.1.1 线性的定义和特点 18 2.1.2 线性的抽象数据类型定义 18 2.2 线性顺序示和实现 19 2.2.1 线性顺序存储示 19 2.2.2 顺序中基本操作的实现 20 2.3 线性的链示和实现 24 2.3.1 单链的定义和示 24 2.3.2 单链基本操作的实现 26 2.3.3 循环 31 2.3.4 双向 32 2.4 线性的应用 34 2.4.1 一般线性的合并 34 2.4.2 有序的合并 35 2.4.3 一元多项示及相加 37 2.5 小结 40 习题 41 第3章 栈和队列 44 3.1 栈 44 3.1.1 栈的类型定义 44 3.1.2 顺序栈的示和实现 45 3.1.3 链栈的示和实现 47 3.2 栈的应用 48 3.2.1 数制转换 49 3.2.2 括号匹配的检验 49 3.2.3 求值 51 3.3 栈与递归 54 3.3.1 采用递归算法解决的问题 54 3.3.2 递归过程与递归工作栈 57 3.3.3 递归算法的效率分析 59 3.3.4 将递归转换为非递归的方法 60 3.4 队列 61 3.4.1 队列的类型定义 61 3.4.2 循环队列——队列的顺序示和实现 62 3.4.3 链队——队列的链示和实现 65 3.5 队列的应用 67 3.6 小结 69 习题 69 第4章 串、数组和广义 73 4.1 串 73 4.1.1 串的类型定义 73 4.1.2 串的存储结构 75 4.1.3 串的模匹配算法 76 4.2 数组 83 4.2.1 数组的类型定义 83 4.2.2 数组的顺序存储 84 4.2.3 特殊矩阵的压缩存储 85 4.3 广义 87 4.3.1 广义的定义 87 4.3.2 广义的存储结构 88 4.4 小结 90 习题 91 第5章 树和二叉树 94 5.1 树的定义和基本术语 94 5.1.1 树的定义 94 5.1.2 树的基本术语 96 5.2 二叉树 97 5.2.1 二叉树的定义 97 5.2.2 二叉树的性质 100 5.2.3 二叉树的存储结构 102 5.3 遍历二叉树和线索二叉树 103 5.3.1 遍历二叉树 103 5.3.2 线索二叉树 109 5.4 树和森林 114 5.4.1 树的存储结构 114 5.4.2 森林与二叉树的转换 116 5.4.3 树和森林的遍历 116 5.5 赫夫曼树及其应用 117 5.5.1 赫夫曼树的基本概念 117 5.5.2 赫夫曼树的构造算法 118 5.5.3 赫夫曼编码 121 5.6 小结 123 习题 123 第6章 图 126 6.1 图的定义和基本术语 126 6.1.1 图的定义 126 6.1.2 图的基本术语 128 6.2 图的存储结构 129 6.2.1 邻接矩阵 130 6.2.2 邻接 132 6.3 图的遍历 135 6.3.1 深度优先搜索 135 6.3.2 广度优先搜索 138 6.4 图的应用 139 6.4.1 最小生成树 139 6.4.2 最短路径 144 6.4.3 拓扑排序 150 6.4.4 关键路径 153 6.5 小结 158 习题 160 第7章 查找 164 7.1 查找的基本概念 164 7.2 线性的查找 165 7.2.1 顺序查找 165 7.2.2 折半查找 166 7.3 树的查找 169 7.3.1 二叉排序树 170 7.3.2 平衡二叉树 176 7.3.3 B-树 182 7.3.4 B+树 190 7.4 散列的查找 192 7.4.1 散列的基本概念 192 7.4.2 散列函数的构造方法 1
本文件内容为数据结构讲义,html格 1 绪论 1、1 什么是数据结构 1、2 基本概念和术语 1、3 抽象数据类型的示与实现 1、4 算法和算法分析 1、4、1 算法 1、4、2 算法设计的要求 1、4、3 算法效率的度量 1、4、4 算法的存储空间需求 2 线性 2、1 线性的类型定义 2、2 线性顺序示和实现 实验一 2、3 线性的链示和实现 2、3、1 线性 2、3、2 循环 实验二 2、3、3 双向 2、4 一元多项示及相加 3 栈和队列 3、1、0 栈 3、1、1 抽象数据类型栈的定义 3、1、2 栈的示和实现 3、2、0 栈的应用举例 3、2、1 数制转换 3、2、2 括号匹配的检验 3、2、3 行编辑程序 实验三 3、2、4 迷宫求解 3、2、5 求值 3、3、0 栈与递归的实现 3、4、0 队列 3、4、1 抽象数据类型队列的定义 3、4、2 链队列-队列的链示和实现 3、4、3 循环队列-队列的顺序示和实现 3、5、0 离散事件模拟 4、0、0 串 4、1、0 串类型的定义 4、2、0 串的示和实现 实验四 4、2、1 定长顺序存储示 4、2、2 堆分配存储示 4、2、3 串的块链存储示 4、3、0 串的模匹配算法 4、3、1 求子串位置的定位函数 4、3、2 模匹配的一种改进算法 4、4、0 串操作应用举例 4、4、1 文本编辑 4、4、2 建立词索引 5、0、0 数组和广义 5、1、0 数组的定义 5、2、0 数组的顺序示和实现 实验五 5、3、0 矩阵的压缩存储 5、3、1 特殊矩阵 5、3、2 稀疏矩阵 5、4、0 广义的定义 5、5、0 广义的存储结构 5、6、0 m元多项示 5、7、0 广义的递归算法 5、7、1 求广义的深度 5、7、2 复制广义 5、7、3 建立广义的存储结构 单元测验 6、0、0 树和二叉树 6、1、0 树的定义和基本术语 6、2、0 二叉树 6、2、1 二叉树的定义 6、2、2 二叉树的性质 6、2、3 二叉树的存储结构 6、3、0 遍历二叉树和线索二叉树 6、3、1 遍历二叉树 实验六 6、3、2 线索二叉树 6、4、0 树和森林 6、4、1 树的存储结构 6、4、2 森林与二叉树的转换 6、4、3 树和森林的遍历 6、5、0 树与等价问题 6、6、0 赫夫曼树及其应用 6、6、1 最优二叉树 6、6、2 赫夫曼编码 6、7、0 回溯法与树的遍历 6、8、0 树的计数 7、0、0 图 7、1、0 图的定义和术语 7、2、0 图的存储结构 7、2、1 数组示法 7、2、2 邻接 7、2、3 十字链 7、2、4 邻接多重 7、3、0 图的遍历 7、3、1 深度优先搜索 7、3、2 广度优先搜索 7、4、0 图的连通性问题 7、4、1 无向图的连通分量和生成树 7、4、2 有向图的强连通分量 7、4、3 最小生成树 7、4、4 关节点和重迦通分量 7、5、0 有向无环图及其应用 7、5、1 拓扑排序 7、5、2 关键路径 7、6、0 最短路径 7、6、1 从某个源点到其余各顶点的最短路径 7、6、2 每一对顶点之间的最短路径 8、0、0 动态存储管理 8、1、0 概述 8、2、0 可利用空间及分配方法 8、3、0 边界标识法 8、3、1 可利用空间的结构 8、3、2 分配算法 8、3、3 回收算法 8、4、0 伙伴系统 8、4、1 可利用空间的结构 8、4、2 分配算法 8、4、3 回收算法 8、5、0 无用单元收集 8、6、0 存储紧缩 9、0、0 查找 9、1、0 静态查找 9、1、1 顺序的查找 9、1、2 有序的查找 9、1、3 静态树的查找 9、1、4 索引顺序的查找 9、2、0 动态查找 9、2、1 二叉排序树和平衡二叉树 9、2、2 B—树和B+树 9、2、3 键树 9、3、0 哈希 9、3、1 什么是哈希 9、3、2 哈希函数的构造方法 9、3、3 处理冲突的方法 9、3、4 哈希的查找及其分析 实验七 10、0、0 内部排序 10、1、0 概述 10、2、0 插入排序 10、2、1 直接插入排序 10、2、2 其它插入排序 10、2、3 希尔排序 10、3、0 快速排序 10、4、0 选择排序 10、4、1 简单选择排序 10、4、2 树形选择排序 10、4、3 堆排序 10、5、0 归并排序 实验八 10、6、0 基数排序 10、6、1 多关键字的排序 10、6、2 链基数排序 10、7、0 各种内部排序方法的比较讨论 11、0、0 外部排序 11、0、1 外存信息的存取 11、0、2 外部排序的方法 11、0、3 多路平衡归并的实现 11、0、4 置换-选择排序 11、0、5 最佳归并树 12、0、0 文件 12、1、0 有关文件的基本概念 12、2、0 顺序文件 12、3、0 索引文件 12、4、0 ISAM文件和VSAM文件 12、4、1 ISAM文件 12、4、2 VSAM文件 12、5、0 直接存取文件(散列文件) 12、6、0 多关键字文件 12、6、1 多重文件 12、6、2 倒排文件 总复习

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落春只在无意间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值