【数据结构】顺序表/链表的运算及实现


前言

1968年美国克努特教授开创了数据结构的最初体系,著作为《计算机程序设计的艺术》。
数据结构研究计算机数据间关系,具体包括数据的逻辑结构、存储结构及数据间运算(操作);
即数据结构把一些复杂的数据理清逻辑关系及存储方式,使其操作更加高效;
数据结构意义:提高编程能力;可复用;维护;可读性;效率;
需要掌握知识:结构体、内存体(malloc)等。
在这里插入图片描述


一、数据结构概念

1.1 数据

数据及信息的载体,是能够输入到计算机中并且能被计算机识别、存储和处理的符号总称。

1.2 数据元素

数据元素是数据的基本单位,又称为记录,数据元素由若干基本项(或称字段、域、属性)组成。
例如:产品编号 产品名称 规格 出厂日期 …
0001 TV 29 1999/09…
数据元素之间存在某种关系:线性关系、层次关系(如上下级)、网状关系

1.3 数据的逻辑结构

逻辑结构 表示数据运算之间的抽象关系,按每个元素可能具有的直接前驱数和直接后继数将数据结构分为“线性结构”和“非线性结构”两大类。
数据元素是一个集合
线性结构——一个对一个,如线性表、栈、队列
树形结构——一个对多个,如树
图状结构——多个对多个,如图

1.4 数据的存储结构

存储结构:逻辑结构在计算机中的具体实现方法。
存储结构是通过计算机语言所编制的程序来实现的,因而是依赖于具体的计算机语言的。
1)顺序存储:将数据结构中各元素按照其逻辑顺序存放于存储器的一片连续的存储空间中;如c语言的一维数组,如表L=(a1,a2,…an)的顺序结构。
**2)链式存储**:将数据结构中各元素分布到存储器的不同点,用地址(或链指针)方式建立它们之间的联系;如:a1 -> a2 -> … ->an
数据结构中元素之间的关系在计算机内部很大程度上是通过地址或指针中来建立的。
3)索引存储:在存储数据的同时,建立一个附加的索引表,即索引存储结构=数据文件+索引表。
4)散列存储:根据数据元素的特殊字段(称为关键字key),计算数据元素的存放地址,然后数据元素按地址存放。

二、线性表

线性表是包含若干数据元素的一个线性序列。
即为:L=(a0,…ai-1,ai,ai+1,…an-1);L为表名;ai为数据元素;n为表长,n>0时,线性表L为非空表,否则为空表。
线性表L可用二元组形式描述:L=(D,R);即线性表L包含数据元素集合D和关系集合R
关系符<ai,ai+1>被称为有序对,ai是ai+1的直接前驱,ai+1是ai的直接后继。
O-O-O-O-O
线性表的特征:
1)对非空表,a0是表头,无前驱
2)an-1是表尾,无后继;
3)其它的每个元素ai有且仅有一个直接前驱ai-1和一个直接后继ai+1。

2.1 顺序表

若将线性表L=(a0,a1,…an-1)中的各元素依此存储于计算机一片连续的存储空间;
设Loc(ai)为ai的地址,Loc(a0)=b,每个元素占d个单元,则Loc(ai)=b+i*d

2.1.1 顺序存储结构特点

优点:
1)逻辑上相邻的元素ai,ai+1,其存储位置也是相邻的;
2)对数据元素ai的存取为随机存取或按地址存取;
3)存储密度高;存储密度D=(数据结构中元素所占存储空间) / (整个数据结构所占空间)。
不足:
1)要求系统提供一片较大的连续存储空间;
2)对表的插入和删除等运算的时间复杂度较差,且存在元素在存储器中成片移动的现象。

2.1.2 顺序存储结构表示

在c语言中,可借助于一维数组类型来描述线性表的顺序存储结构(顺序表)。

顺序表代码如下(示例):

#define N 100
typedef int data_t;  //typedef使得类型(如int)可灵活;data_t是int别名
typedef struct       
{              //typedef,两个成员,一个代表数据是存储元素的,另一个代表实际的有效的最后那个元素的下标
data_t data[N];
int last;        //last是记录最后一个元素下标,属于0-100
}sqlist,*sqlink;      //sqlist是结构体,*sqlink是结构体指针

2.1.3 线性表的基本运算

设线性表L=(a0,a1,…an-1),对L的基本运算有:
1)建立一个空表:L list_create();
2)置空表:list_clear(L);
3)判断表是否为空:list_empty(L)。若表为空,返回值为1,否则返回0;
4)求表长:length(L);
5)取表中某个元素:GetList(L,i),即ai。要求0<=i<=length(L)-1;
6)定位运算:Locate(L,x)。确定元素x在表L中的位置(或序号);
如果在线性表中,返回元素在线性表中的位置,即下标 i;不在返回-1.
7)插入:Insert(L,x,i),将元素x插入到表L中第i个元素ai之前,且表长+1。
在这里插入图片描述
8)删除:Delete(L,i)。删除表L中第i个元素ai,且表长减1,要求0<=i<=n-1。
9)线性表并集:依次取出线性表Lb中的每个元素,若bi不属于La,则插入表La中;(重复元素不处理)
10)清除线性表重复元素:

2.1.4 顺序表的实现

文件夹复制命令:cp sqlist/ work -r
命令行模式下:vsp 分层;
一步生成最终链接后的文件:*gcc .c -o test;
索引从0开始,L-> = -1表示为空表
顺序表插入元素特点:
1)线性表是否满,有无空间;
2)元素插入位置属于[0,last+1];
3)元素移动,在前面插入元素,其它元素向后移动;
4)存新值,更新last。
顺序表删除元素特点:
空表的话,last->=-1
1)检测元素是否在范围中,pos属于[0,last];
2)元素移动,删除元素的紧挨着元素位置往前移动;
3)更新。l
顺序表并集运算特点:
1)依次取表Lb中元素;
2)检查bi是否在La中;
3)如果不在,插入进去La.
(2)(3)循环进行。
顺序表重复删除特点:
1)依次取i,i属于[0,last];
2)检查ai是否在[0,i-1]中:不在i++,在删除i;

头文件代码< sqlist.h >:

typedef int data_t;
#define N 128
typedef struct
{
	data_t data[N];
	int last;
}sqlist, *sqlink;

sqlink list_create();
int list_clear(sqlink L);
int list_free(sqlink L); //销毁
int list_empty(sqlink L);
int list_length(sqlink L);
int list_locate(sqlink L,data_t value);
int list_insert(sqlink L,data_t value,int pos);
int list_delete(sqlink L, int pos);
int list_merge(sqlink L1,sqlink L2);
int list_purge(sqlink L);
int list_show(sqlink L);

1)插入元素测试代码
>1.1 main文件:

#include <stdio.h>
#include "sqlist.h"
void test_insert();//封装 测试insert函数
int main(int argc, char *argv[])
{
	test_insert();
	return 0;
}
void test_insert()
{
	sqlink L;
	L=list_create();
	if (L == NULL)
		return;

	list_insert(L, 10, 0); //插入10
	list_insert(L, 20, 0); //插入20
	list_insert(L, 30, 0); //插入30
	list_insert(L, 40, 0); //插入40
	list_insert(L, 50, 0); //插入50
	list_insert(L, 60, 0); //0插入60
	//依次往后移,结果为60 50 40 30 20 10
	list_show(L);
	
	list_insert(L, 100, list_length(L)); 
	//目前长度为6,=(L,100,6)
	
	list_insert(L,100,-10000); //无效
	list_show(L);

	list_free(L);
}

>1.2 函数代码.c文件:

#include <stdio.h>
#include "sqlist.h"
#include <stdlib.h>
#include <string.h>
sqlink list_create()
{
    //malloc 第一步申请内存
	 sqlink L;   //sqlink结构体指针 
	 L =(sqlink)malloc(sizeof(sqlist));    //sqlist代表整个结构体
	if (L == NULL)  //判断是否成功
	{
		printf("list malloc failed\n");
		return L;
	}
	
	//initialize 第二步初始化
	 memset(L, 0, sizeof(sqlist));  //清零
	 L->last = -1;  //L是结构体,结构体指针访问结构体成员用箭头

	//return  第三步返回值
	return L;
}

//返回0 success -1 failed
int list_clear(sqlink L)
{
	if (L == NULL)
		return -1;

	 memset(L, 0, sizeof(sqlist));  //填充0
	 L->last = -1; //表示为空表

	return 0;
}

int list_free(sqlink L) //释放空间
{
	if (L == NULL)
		return -1;
	free(L);
	L = NULL;
	return 0;

}
//check list empty?  1-empty 0-not empty
int list_empty(sqlink L)
{
	if (L->last == -1)
		return 1;
	else
		return 0;
}

int list_length(sqlink L)
{
	if (L == NULL)
		return -1;
    return (L->last+1);
}

// -1 not exit;判断value在不在线性表中
int list_locate(sqlink L,data_t value)
{
		int i;
		for (i=0;i<=L->last;i++)
		{
			if (L->data[i] == value)
				return i;
		}
		return -1;
}

int list_insert(sqlink L, data_t value, int pos)
{
	int i;

    //1) judge full
	if (L->last == N-1)
	{
		printf("list is full\n");
		return -1;
	}
	//2)check para pos  0<=pos<=last+1
	if (pos < 0 || pos > L->last+1)
	{
		printf("pos is invalid\n");
		return -1;
	}

	//3)mov
	for (i = L->last; i >= pos; i--)
	{
		L->data[i+1] = L->data[i];
	}

   //4)update value last
	L->data[pos] = value;
	L->last++;
	
	return 0;
}
int list_show(sqlink L)
{
	int i;
	if (L == NULL)
		return -1;
	if(L->last == -1 )
		printf("list is empty\n");

	for (i = 0;i <= L->last; i++)
	{
		printf("%d ", L->data[i]);
	}
	puts("");
	return 0;
}

结果展示:
在这里插入图片描述

2)删除元素测试代码
>2.1 main文件:

#include <stdio.h>
#include "sqlist.h"
void test_delete();
int main(int argc, char *argv[])
{
	test_delete();
	return 0;
}
void test_delete()
{
	sqlink L;
	L = list_create();
	if (L == NULL)
		return;

	list_insert(L, 10, 0); 
	list_insert(L, 20, 0); 
	list_insert(L, 30, 0); 
	list_insert(L, 40, 0); 
	list_insert(L, 50, 0); 
	list_insert(L, 60, 0); 

	list_show(L);

	list_delete(L, 2);
	list_show(L);

	list_free(L);
}

>2.2 函数代码.c文件:

前面代码可直接复制.c文件

int list_delete(sqlink L, int pos)
{
	int i;
    if (L->last == -1)
	{
		printf("list is empty\n");
		return -1;
	}
	// pos [0, last]
	if (pos < 0 || pos > L->last)
	{
		printf("delete pos is invalid\n");
		return -1;
	}
	// mov [pos+1,last]
	for (i=pos+1;i<=L->last;i++)
	{
		L->data[i-1]=L->data[i];
	}
	// update
	L->last--;

	return 0;
}

结果展示:
在这里插入图片描述
3)线性表并集运算测试代码(不合并重复元素)
>3.1 main文件:

#include <stdio.h>
#include "sqlist.h"
void test_merge();
int main(int argc, char *argv[])
{
	test_merge();
	return 0;
}

void test_merge()
{
	sqlink	L1, L2;

	L1 = list_create();
	if (L1 == NULL)
		return;
	L2 = list_create();
	if (L2 == NULL)
		return;

	list_insert(L1, 10, 0); 
	list_insert(L1, 20, 0); 
	list_insert(L1, 30, 0); 
	list_insert(L1, 40, 0); 

	list_insert(L2, 50, 0); 
	list_insert(L2, 20, 0); 
	list_insert(L2, 50, 0);
	list_insert(L2, 40, 0); 

	list_show(L1);
	list_show(L2);
	printf("***************\n");

	list_merge(L1, L2);

	list_show(L1);
	list_show(L2);
}

>3.2 函数代码.c文件:

前面代码可直接复制.c文件

int list_merge(sqlink L1,sqlink L2)
{
	int i = 0;
	int ret;

	while (i <= L2->last)  //遍历
	{
		ret = list_locate(L1, L2->data[i]);
		if (ret == -1)
		{
			if (list_insert(L1,L2->data[i],L1->last+1) == -1)
				return -1;  //如果L1满了,insert==-1不能再插入
		}
		i++;
	}
	return 0;
}

结果展示:
在这里插入图片描述

4)线性表删除重复元素测试代码
>4.1 main文件:

#include <stdio.h>
#include "sqlist.h"
void test_purge();
int main(int argc, char *argv[])
{
	test_purge();
	return 0;
}
void test_purge()
{
	sqlink L;
	L = list_create();
	if (L == NULL)
		return;

	list_insert(L, 10, 0); 
	list_insert(L, 20, 0); 
	list_insert(L, 30, 0); 
	list_insert(L, 20, 0); 
	list_insert(L, 50, 0); 
	list_insert(L, 20, 0); 
	
	list_show(L);

	list_purge(L);
	list_show(L);

	list_free(L);
}

>4.2 函数代码.c文件:

int list_purge(sqlink L)
{   
	int i;
	int j;
	if (L->last == 0)
		return 0;
	i=1;
	while (i <= L->last)
	{
		j=i-1;
		while (j >= 0)
		{
			if (L->data[i] == L->data[j])
			{
				list_delete(L, i);
				break;
			}
			else
			{
				j--;	
			}
		}
		 if (j < 0)  //如果没找到 
	    {
			i++;
	    }	
	}
 	return 0;
}

结果展示:
在这里插入图片描述

2.1 链表

单链表优点:
1)动态数据。结构链表是一个动态数据结构,无需给出链表的初始大小;
2)插入和删除。插入或删除元素后不必移动元素,只需要更新节点的下一个指针中存在的地址;
3)没有记忆浪费。仅在需要时才分配内存。
单链表缺点:
1)内存使用情况。在链表中存储元素需要更多的内存。 因为在链表中,每个节点都包含一个指针,并且它本身需要额外的内存;
2)遍历。在链表中很难遍历元素或节点。 不能像按索引在数组中那样随机访问任何元素。 例如,如果我们要访问位置n处的节点,则必须遍历它之前的所有节点。 因此,访问节点所需的时间很大;
3)反向遍历困难,在链表中,反向遍历确实很困难。 在双链表的情况下,它比较容易,但是向后指针需要额外的内存,因此浪费了内存。

2.2.1 链式存储结构

将线性表L={a0,a1,…,an-1}中各元素分布在存储器的不同存储器的不同存储块,称为结点,通过地址或指针简历元素之间的联系。在这里插入图片描述

结点的data域存放数据元素ai,而next域是一个指针,指向ai的直接后继ai+1所在的结点。
在这里插入图片描述

2.2.2 结点类型描述

Listnode相当于typedef struct node
在这里插入图片描述
P =(强转)malloc(sizeof(listnode)) //把结点放在栈上
p->data = value
把变量放到栈上,赋值:
1)A.data = value 2)用指针写 p->data = value
->:通过结构体指针访问结构体成员

在这里插入图片描述
动态内存申请
可调用C语言中malloc()函数向系统申请结点的存储空间
linklist p;
p = (linklist)malloc(sizeof(listnode));

则创建一个类型为linklist的结点,且该结点的地址已存入指针变量p中:
在这里插入图片描述

2.2.3 链表的基本运算

建立单链表
依次读入表在这里插入图片描述中每一元素ai(假设为整型),若ai不等于结束符(-1),则为ai创建一结点,然后插入表尾,最后返回链表的头结点指针H。
在这里插入图片描述

链表查找
1)按序号查找:实现list_get(h,i)运算。
算法思路:从链表的a0起,判断是否为第i结点,若是则返回该结点的指针,否则查找下一结点,依次类推。
2)按值查找(定位):即实现locate(h,x)。
算法思路:从链表结点a0起,依次判断某结点是否等于x,若是,则返回该结点的地址;若不是,则查找下一结点a1,依次类推。若表中不存在x,则返回NULL。
在这里插入图片描述

链表插入
实现list_insert(h,x,i)。将x插入表中结点ai之前的情况。
算法思路:调用算法list_get(h,i-1),获得结点ai-1的指针p(ai之前驱),然后申请一个q结点,存入x,并将其插入p指向的结点之后。
a. 调用list_get(h,i-1);
b. 插入:i) 新结点;ii) 两个指向更新。
在这里插入图片描述
*尾部插入和遍历
i)建一个新结点、 malloc、 赋值、
ii)找到尾结点 定位尾结点
iii)尾部插入
在这里插入图片描述

2.2.4 链表实现

## 链表的创建和元素插入
在这里插入图片描述

在这里插入图片描述
>头文件代码< linklist.h >:

typedef int data_t;

typedef struct node
{
	data_t data;
	struct node * next;
}listnode,* linklist;

linklist list_create();
int list_tail_insert(linklist H,data_t value);//head
linklist list_get(linklist H,int pos);//找到前一个结点
int list_insert(linklist H,data_t value,int pos);
int list_show(linklist H);

>主函数文件< test.c >:

#include <stdio.h>
#include "linklist.h"

void test_get();  

int main(int argc, char *argv[])
{
	linklist H;
	int value;
	linklist p;

	H = list_create();
	if (H == NULL)
		return -1;

	printf("input:");
	while (1)
	{
		scanf("%d",&value);
		if (value == -1)//如果输入-1结束
			break;
		list_tail_insert(H,value);
		printf("input:");
	}

	list_show(H);
	list_insert(H,100,2); //找第二个位置上的值
	list_show(H);

	return 0;
}

void test_get()
{
	linklist H;
	int value;
	linklist p;
	H = list_create();
	if (H == NULL)
		return;

	printf("input:");
	while (1)
	{
		scanf("%d",&value);
		if (value == -1)//如果输入-1结束
			break;
		list_tail_insert(H,value);
		printf("input:");
	}
	list_show(H);

	p = list_get(H,2); //找第二个位置上的值
	if (p != NULL)
	printf("value=%d\n", p->data);
}

>子函数实现< linklist.c >:

#include <stdio.h>
#include "linklist.h"
#include <stdlib.h>

linklist list_create()
{
	linklist H;
	H = (linklist)malloc(sizeof(listnode));
	if (H == NULL)
	{
		printf("malloc failed\n");
		return H;
	}

	H->data = 0;
	H->next = NULL;

	return H;
}
int list_tail_insert(linklist H,data_t value)
{
	linklist p;
	linklist q;
	if (H == NULL)
	{
		printf ("H is NULL");
		return -1;
	}
	//1) new node p
	if ((p = (linklist)malloc(sizeof(listnode))) == NULL)
	{
		printf("malloc failed\n");
		return -1;
	}
	p->data = value;
	p->next =NULL;
	
	//2 locate tail node
	q = H;
	while(q->next != NULL)
	{
		q=q->next;
	}
	//3 inserta
	q->next = p;

	return 0;
}

linklist list_get(linklist H,int pos)//找到前一个结点
{
	linklist p;
	int i;
	if (H == NULL)
	{
		printf("H is NULL");
		return NULL;
	}
	if (pos == -1)
	{
		return H;
	}

	p = H;
	i = -1;
	while (i<pos)
	{
		p=p->next;
		if (p == NULL)
		{
			printf("pos is invalid\n");
			return NULL;
      	}
		i++;
	}
	return p;
}
int list_insert(linklist H,data_t value,int pos)
{
	linklist p;
	linklist q;
	if (H == NULL)
	{
		printf("H is NULL");
		return -1;
	}
//1 locate node pose-1
	p = list_get(H,pos-1);
	if (p == NULL)
	{
		return -1;
	}
//2 new node q
	if ((q = (linklist)malloc(sizeof(listnode))) == NULL)
	{
		printf("malloc failed\n");
		return -1;
	}
	q->data = value;
	q->next = NULL;

//3 inser
	q->next = p->next; 
	p->next = q; //q放p后面
	return 0;
}

int list_show(linklist H)
{
	linklist p;
	if (H == NULL)
	{
		printf("H is NULL\n");
	}
	p = H;
	while (p->next != NULL)
	{
		printf("%d ", p->next->data);
		p = p->next;
	}
	puts("");
	return 0;
}

结果展示:
在这里插入图片描述

< 链表特定元素删除 >
算法思路:*
如果删除ai,先找的箭头起点(待删除结点前驱) 再找终点
p->data 是ai-1,p->next是其地址
1)定位,先找前驱;
2)q=p->next 用q存储待删除结点 赋值
3)p->next = p->next->next 改指针
因此p->next = q->next
4)free(q) 释放
在这里插入图片描述
>主函数文件< test.c >:

#include <stdio.h>
#include "linklist.h"

int main(int argc, char *argv[])
{
	linklist H;
	int value;
	linklist p;

	H = list_create();
	if (H == NULL)
		return -1;

	printf("input:");
	while (1)
	{
		scanf("%d",&value);
		if (value == -1)//如果输入-1结束
			break;
		list_tail_insert(H,value);
		printf("input:");
	}

	list_show(H);
	list_delete(H, 0);//删除第0个位置元素
	list_show(H);
	list_free(H);  //释放空间

	return 0;
}

>子函数实现< linklist.c >:

int list_delete(linklist H, int pos) {
	linklist p;
	linklist q;
	//1
	if (H == NULL) {
		printf("H is NULL\n");
		return -1;
	}
	//2 locate prior
	p = list_get(H, pos-1);
	if (p == NULL) 
		return -1;
	if (p->next == NULL) {
		printf("delete pos is invalid\n");
		return -1;
	}
	//3 update list
	q = p->next;
	p->next = q->next;//p->next = p->next->next;

	//4 free
	printf("free:%d\n", q->data);
	free(q);
	q = NULL;
	return 0;
}

结果展示:
在这里插入图片描述
< 链表所有元素删除(即释放链表所有空间) >
>主函数文件< test.c >:

#include <stdio.h>
#include "linklist.h"
int main(int argc, const char *argv[])
{
	linklist H;
	int value;
	
	H = list_create();
	if (H == NULL)
		return -1;

	printf("input:");
	while (1) {
		scanf("%d", &value);
		if (value == -1)
			break;
		list_tail_insert(H, value);
		printf("input:");
	}

	list_show(H);
	printf("H=%p\n", H);
	H = list_free(H);
	printf("H=%p\n", H);
	list_delete(H, 0);
	 //此时加不加特定元素删除都可,由于上面已经释放完所有空间,因此在加上删除语句后会打印出“H is NULL”
//	list_show(H);  链表所有元素已经被释放,加上该句会打印报错

	list_free(H);
	
	return 0;
}

>子函数实现< linklist.c >:

linklist list_free(linklist H) {
	linklist p;
	if (H == NULL) 
		return NULL;

	p = H;

	printf("free:");
	while (H != NULL) {
		p = H;
		printf("%d ", p->data);
		free(p);
		H = H->next;
	}
	puts("");
	return NULL;
}

结果展示:
在这里插入图片描述

< 链表倒置(元素反序输出) >
算法思路:
依次取原链表中各结点,将其作为新链表首结点插入H结点之后
在这里插入图片描述
实现分析:
1)排除情况:i) H是空;ii) 是个空链表,没有数据;iii)只有一个结点
2)i) 指针指向第二个结点; ii) H->next->next(第二个结点地址)置空
在这里插入图片描述

3)将取出结点放在H后面
在这里插入图片描述
>主函数文件< test.c >:

#include <stdio.h>
#include "linklist.h"
int main(int argc, const char *argv[])
{
	linklist H;
	int value;
	H = list_create();
	if (H == NULL)
		return -1;
	printf("input:");
	while (1) {
		scanf("%d", &value);
		if (value == -1)
			break;
		list_tail_insert(H, value);
		printf("input:");
	}

	list_show(H);
	list_reverse(H); //调用该子函数
	list_show(H);	
	list_free(H);
	return 0;
}

>子函数实现< linklist.c >:

int list_reverse(linklist H) 
{
	linklist p;
	linklist q;
	if (H == NULL) 
	{
		printf("H is NULL\n");
		return -1;
	}
	if (H->next == NULL || H->next->next == NULL) 
	{
		return 0;
	}
	p = H->next->next;
	H->next->next = NULL;

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

		q->next = H->next;
		H->next = q;
	}
	return 0;
}

结果展示:
在这里插入图片描述
< 设结点data域为整型,求链表中相邻两结点data值之和为最大的第一结点的指针 >
算法思路:
设q,p分别为链表中相邻两结点指针,求q->data + p->data为最大的那一组值,返回其相相应的指针q即可。
补充:
同时输出结点相加之和summ。
在这里插入图片描述
实现分析:
返回值为指针,求最大的一组和需要遍历
1)保证结点个数大于2,<=2退出
2)构建变量p q sum 变量初始化
3)p=p->next q=q->next往后移动 (需要循环处理)
最后更新!
在这里插入图片描述
>主函数文件< test.c >:

#include <stdio.h>
#include "linklist.h"
int main(int argc, const char *argv[])
{
	linklist H;
	int value;
	int summ;  //求结点和
	H = list_create();
	if (H == NULL)
		return -1;
	printf("input:");
	while (1) {
		scanf("%d", &value);
		if (value == -1)
			break;
		list_tail_insert(H, value);
		printf("input:");
	}

	list_show(H);
	
	r = list_adjmax(H,&summ);
	if (r != NULL && r != summ)
	{
		printf("data=%d, summ=%d\n", r->data,summe);
	}
		
	list_show(H);	
	list_free(H);
	return 0;
}

>子函数实现< linklist.c >:

linklist list_adjmax(linklist H, data_t *value) 
{
	linklist p, q, r;
	data_t sum;
	if (H == NULL){
		printf("H is NULL\n");
		return NULL;
	}

	if (H->next == NULL || H->next->next == NULL || H->next->next->next == NULL) 
	{
		return H;
	}

	q = H->next;
	p = H->next->next;//p = q->next;
	r = q;     //r记录返回值(返回值为指针),存储第一个结点
	sum = q->data + p->data;  //

	while (p->next != NULL)
	 {
		p = p->next; //表示p往后移动
		q = q->next;
		if (sum < q->data + p->data) //如果发现更大的和,比原来大
		 {
			sum = q->data + p->data;  //更新sum值
			r = q;    //r记录第一个结点,一直到p->next为空
		}
	}
	*value = sum;  //返回两个相邻结点值的最大和与第一个结点的指针值
	return r;  
//上面if条件不总是满足,只有和为最大时才保存,所以返回的是r,而不是q
}

结果展示:
在这里插入图片描述

< 两个链表合并,且按data值递增有序>
算法思路:
设指针p,q分别指向表A和B中的结点,若p->data <= q->data,则p结点进入结果表,否则q结点进入结果表。
在这里插入图片描述
实现分析:
在这里插入图片描述
>主函数文件< test.c >:

#include <stdio.h>
#include "linklist.h"
int main(int argc, const char *argv[])
{
	linklist H1, H2;
	int a[] = {1, 4, 6, 8, 10};
	int b[] = {2, 4, 16, 18, 30};
	int i;

	H1 = list_create();
	if (H1 == NULL)
		return;
	H2 = list_create();
	if (H2 == NULL)
		return;

	for (i = 0; i < sizeof(a)/sizeof(int); i++) {
		list_tail_insert(H1, a[i]);
	}
	for (i = 0; i < sizeof(b)/sizeof(int); i++) {
		list_tail_insert(H2, b[i]);
	}

	list_show(H1);
	list_show(H2);

	list_merge(H1, H2);
	printf("merge:\n");

	list_show(H1);
	list_show(H2);

	list_free(H1);
	list_free(H2);
	return 0;
}

>子函数实现< linklist.c >:

int list_merge(linklist H1, linklist H2)
{
	linklist p, q, r;
	if (H1 == NULL || H2 == NULL) 
	{
		printf("H1 || H2 is NULL\n");
		return -1;
	}
	p = H1->next;
	q = H2->next;
	r = H1;
	H1->next = NULL;
	H2->next = NULL;

	while (p && q)
	 {
		if (p->data <= q->data) 
		{
			r->next = p;
			p = p->next;
			r = r->next;
			r->next = NULL;
		} 
		else 
		{
			r ->next = q;
			q = q->next;
			r = r->next;
			r->next = NULL;
		}
	}
	if (p == NULL) 
	{
		r->next = q;
	}
	else 
	{
		r->next = p;
	}
	return 0;
}

结果展示:
在这里插入图片描述

<题目:实现链表的排序(使链表中元素的从无序到有序,要求从小到大)>
**算法思路:**用指针指向链表第一个结点,把头结点next置空,取出一个结点往前面放,依次进行。
>子函数实现< linklist.c >:

int list_rank(linklist H1)
{
	linklist p,q,r;
	if (H1 == NULL )
	{
		printf("H1 is NULL\n");
		return -1;
	}
	p = H1->next;
	//q = p->next;
	r = H1;
	H1->next = NULL; //首结点后断开

	while (p)
	{
		r = p->next; //r指向下一个元素
//H1开始移动,查找p插入位置
		while ((H1->next) && (H1->next->data)<(p->data))
		{
			H1 = H1->next;	
		}
//p插入H1后
		p->next = H1->next;
		H1->next = p;
		p = r;
	}
	return 0;
}

结果展示:
在这里插入图片描述

个人学习记录!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
链表是一种常见的数据结构,用于存储和组织数据。它由节点组成,每个节点包含一个值和一个指向下一个节点的指针。链表的节点可以按顺序链接,形成一个链式结构。 链表有多种类型,包括单链表、双链表和循环链表。单链表每个节点只有一个指向下一个节点的指针;双链表每个节点有两个指针,分别指向前一个节点和后一个节点;循环链表最后一个节点的指针指向第一个节点,形成一个循环。 链表的优点是插入和删除操作的时间复杂度为O(1),因为只需要修改节点的指针即可。而数组在插入和删除时需要移动其他元素,时间复杂度为O(n)。链表的缺点是访问某个节点的时间复杂度为O(n),因为需要从头节点开始遍历。 链表在许多应用都有广泛的应用。一些常见的应用包括: 1. 实现栈和队列:链表可以用来实现栈和队列这样的数据结构,其栈是一种后进先出(LIFO)的结构,队列是一种先进先出(FIFO)的结构。 2. 内存分配:动态内存分配时,链表可以用来管理空闲内存块,以便高效地分配和释放内存。 3. 图的表示:链表可以用来表示图的邻接表,其每个顶点都用一个链表来存储与其相邻的顶点。 4. 多项式求解:链表可以用来表示多项式,并进行多项式加法、乘法等运算。 5. 缓存实现链表可以用来实现缓存,其最近使用的数据节点被放在链表的头部,最久未使用的数据节点被放在链表的尾部。 这些只是链表应用的一些例子,实际上链表在计算机科学和软件工程有许多其他的应用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值