数据结构第二讲:线性结构

参考:浙大数据结构(陈越、何钦铭)课件

1、线性表及其实现

有一个很好的问题可以方便的说明引入链表的好处,那就是一元多项式:f(x) = a0 + a1x + an-1xn-1 + anxn 的表示及运算(两个多项式相加/相减/相乘等),显然我们可以利用数组来解决这个问题,两个多项式相加就是两个数组对应分量相加。但是这引入了一个很严重的问题,如何表示多项式x + 3x2000呢?开这么大的数组明显会造成空间浪费。

解决上面遗留的问题的一个方法是用结构数组按指数大小有序存储,每一个数组元素维护两个信息:多项式每一项的系数和指数。执行相加操作的时候只需要从头开始比较两个多项式当前对应项的指数即可。但是还是有问题,数组的物理空间是连续的,如果我们的数据超过了程序可分配的最大连续空间,那就杯具了,这很自然的引入了我们的链表:

链表的存储结构,即每个节点包含系数和指数两个数据域以及一个指针域:

typedef struct PolyNode * Polynomial;
typedef struct PolyNode{
    int coef;
    int expon;
    Polynomial link;
}

多项式表示问题给我们的启示就是:

  • 同一个问题可以有不同的表示(存储)方法
  • 有一类共性问题:有序线性序列的组织和管理

线性表(Linear List):由同类型数据元素构成有序序列的线性结构

  • 表中元素个数称为线性表的长度
  • 线性表没有元素时,称为空表
  • 表起始位置称为表头,结束位置称为表尾

线性表基本操作有:

  • List MakeEmpty():初始化一个空线性表
  • ElementType FindeKth(int K, List L):根据位序K,返回相应元素
  • int Find(ElementType X, List L):在线性表L中查找X的第一次出现位置
  • void Insert(ElementType X, int i, List L):在位序i 前插入一个新元素X
  • void Delete(int i, List L):删除指定位序i 的元素
  • int Length(List L): 返回线性表L 的长度n

线性表的顺序存储实现(利用数组的连续存储空间顺序存放线性表的各个元素):

存储结构:

typedef struct{
	ElementType Data[MAXSIZE];
	int Last;
}List;
List L, *PtrL;

访问下标为i 的元素:L.Data[i] 或PtrL->Data[i]

线性表的长度:L.Last+1 或PtrL->Last + 1(Last 表示末尾元素的下标位置)

主要操作实现:

  • 初始化(建立空表)
List * MakeEmpty()
{
	List *PtrL;
	PtrL = (List *)malloc(sizeof(List));
	PtrL->Last = -1;
	return PtrL;
}
  • 查找
int Find(ElementType X, List *PtrL)
{
	int i = 0;
	while(i <= PtrL->Last && PtrL->Data[i] != X)
		i++;
	if(i > PtrL->Last)
		return -1; // 如果没找到,返回-1
	else
		return i; // 找到后返回的是存储位置
}
  • 插入(第i(1 ≤ i ≤ n+1)个位置上插入一个值为X的新元素) 
void Insert(ElementType X, int i, List *PtrL)
{
	int j;
	if(PtrL->Last == MAXSIZE-1) // 表空间已满,不能插入
	{
		printf("表满");
		return;
	}
	if(i < 1 || i > PtrL->Last+2)
	{
		printf("位置不合法");
		return;
	}
	for(j = PtrL->Last; j >= i-1; j--)
		PtrL->Data[j+1] = PtrL->Data[j]; // 将ai~an倒序向后移动
	PtrL->Data[i-1] = X; // 新元素插入
	PtrL->Last++; // Last仍指向最后元素
	return;
}
  • 删除(删除表的第i(1 ≤ i ≤ n) 个位置上的元素)
void Delete(int i, List *PtrL)
{
	int j;
	if(i < 1 || i > PtrL->Last+1)
	{
		printf("不存在第%d个元素", i);
		return;
	}
	for(j = i; j <= PtrL->Last; j++)
		PtrL->Data[j-1] = PtrL->Data[j]; // 将ai+1~an顺序向前移动
	PtrL->Last--; // Last仍指向最后元素
	return;
}

线性表的链式存储实现(不要求逻辑上相邻的两个元素物理上也相邻,通过"链"建立起数据元素之间的逻辑关系):

存储结构:

typedef struct Node{
	ElementType Data;
	struct Node *Next;
}List;
List L, *PtrL;

主要操作实现

  • 求表长
int Length(List *PtrL)
{
	List *p = PtrL; // p指向表的第一个结点
	int j = 0;
	while(p)
	{
		p = p->Next;
		j++; // 当前p指向的是第j个结点
	}
	return j;
}
  • 按序号查找
List *FindKth(int K, List *PtrL)
{
	List *p = PtrL;
	int i = 1;
	while(p != NULL && i < K)
	{
		p = p->Next;
		i++;
	}
	if(i == K) // 找到第K个,返回指针
		return p;
	else
		return NULL; // 否则返回空指针
}
  • 按值查找
List *Find(ElementType X, List *PtrL)
{
	List *p = PtrL;
	while(p != NULL && P->data != X)
		p = p->Next;
	return p;
}
  • 插入(在第i-1(1 ≤ i ≤ n+1)个结点后插入一个值为X的新结点)  
List *Insert(ElementType X, int i, List *PtrL)
{
	List *p, *s;
	if(i == 1) // 新结点插入在表头
	{
		s = (List *)malloc(sizeof(List)); // 申请、填装结点
		s->Data = X;
		s->Next = PtrL;
		return s;
	}
	p = FindKth(i-1, PtrL); // 查找第i-1个节点
	if(p == NULL) // 第i-1个不存在,不能插入
	{
		printf("参数i错");
		return NULL;
	}
	else
	{
		s = (List *)malloc(sizeof(List)); // 申请、填装结点
		s->Data = X;
		s->Next = p->Next; // 新结点插入在第i-1个结点的后面
		p->Next = s;
		return PtrL;
	}
}
  • 删除(删除链表的第i(1 ≤ i ≤ n)个位置上的结点)  
List *Delete(int i, List *PtrL)
{
	List *p, *s;
	if(i == 1) // 若要删除的是表的第一个结点
	{
		s = PtrL; // s指向第1个结点
		if(PtrL != NULL)
			PtrL = PtrL->Next; // 从链表中删除
		else
			return NULL;
		free(s);
		return PtrL;
	}
	p = FindKth(i-1, PtrL); // 查找第i-1个结点
	if(p == NULL)
	{
		printf("第%d个结点不存在", i-1);
		return NULL;
	}
	else if(p->Next == NULL)
	{
		printf("第%d个结点不存在", i);
		return NULL;
	}
	else
	{
		s = p->Next; // s指向第i个结点
		p->Next = s->Next; // 从链表中删除
		free(s); // 释放被删除结点
		return PtrL;
	}
}

2、堆栈

栈有很多酷炫的应用:表达式求值,括号匹配,函数调用与递归实现,深度优先搜索,回溯算法,不一而足。

堆栈的抽象数据类型描述

堆栈:具有一定操作约束的线性表,只在一端(栈顶,Top)做插入、删除

插入数据:入栈(Push);删除数据:出栈(Pop);后入先出:List In First Out(LIFO)

基本操作有:

  • Stack CreateStack(int MaxSize):生成空堆栈,其最大长度为MaxSize
  • int IsFull(Stack S, int MaxSize):判断堆栈S是否已满
  • void Push(Stack S, ElementType item):将元素item压入堆栈
  • int IsEmpty(Stack S):判断堆栈S是否为空
  • ElementType Pop(Stack S):删除并返回栈顶元素

栈的顺序存储实现(通常由一个一维数组和一个记录栈顶元素位置的变量组成):

#define MaxSize <储存数据元素的最大个数>
typedef struct{
	ElementType Data[MaxSize];
	int Top;
}Stack;

// 入栈
void Push(Stack *PtrS, ElementType item)
{
	if(PtrS->Top == MaxSize-1)
	{
		printf("堆栈满");
		return;
	}
	else
	{
		PtrS->Data[++(PtrS->Top)] = item;
		return;
	}
}

// 出栈
ElementType Pop(Stack *PtrS)
{
	if(PtrS->Top == -1)
	{
		printf("堆栈空");
		return ERROR; // ERROR是ElementType的特殊值,标志错误
	}
	else
		return (PtrS->Data[(PtrS->Top)--])
}

[实例]请用一个数组实现两个堆栈,要求最大的利用数组空间,使数组只要有空间入栈操作就可以成功。

  分析:一种比较聪明的方法是使这两个栈分别从数组的两头开始向中间生长;当两个栈的栈顶指针相遇时,表示两个栈都满了。

#define MaxSize <存储数据元素的最大个数>
struct DStack{
	ElementType Data[MaxSize];
	int Top1; // 堆栈1的栈顶指针
	int Top2; // 堆栈2的栈顶指针
}S;

S.Top1 = -1;
S.Top2 = MaxSize;

// 入栈
void Push(struct DStack *PtrS, ElementType item, int Tag)
{
	// Tag作为区分两个堆栈的标志,取值为1和2
	if(PtrS->Top2 - PtrS->Top1 == 1) // 堆栈满
	{
		printf("堆栈满");
		return;
	}
	if(Tag == 1) // 对第一个堆栈操作
		PtrS->Data[++(PtrS->Top1)] = item;
	else // 对第二个堆栈操作
		PtrS->Data[--(PtrS->Top2)] = item;
}

// 出栈
ElementType Pop(struct DStack *PtrS, int Tag)
{
	if(Tag == 1)
	{
		if(PtrS->Top == -1) // 堆栈1空
		{
			printf("堆栈1空"):
			return NULL;
		}
		else
			return  PtrS->Data[(PtrS->Top1)--];
	}
	else
	{
		if(PtrS->Top2 == MaxSize) // 堆栈2空
		{
			printf("堆栈2空");
			return NULL;
		}
		else
			return PtrS->Data[(PtrS->Top2)++];

	}
}

堆栈的链式存储实现:

  栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在链栈的栈顶进行。值得思考的一个问题是:栈顶指针应该在链表的哪一头?

typedef struct Node{
	ElementType Data;
	struct Node *Next;
}LinkStack;
LinkStack *Top;

LinkStack *CreateStack()
{
	// 构建一个堆栈的头节点,返回指针
	LinkStack *S;
	S = (LinkStack *)malloc(sizeof(struct Node));
	S->Next = NULL;
	return S;
}

int IsEmpty(LinkStack *S)
{
	// 判断堆栈S是否为空,若为空返回整数1,否则返回0
	return S->Next == NULL;
}

void Push(ELementType item, LinkStack *S)
{
	// 将元素item压入堆栈S
	struct Node *TmpCell;
	TmpCell = (LinkStack *)malloc(sizeof(struct Node));
	TmpCell->Element = item;
	TmpCell->Next = S->Next;
	S->Next = TmpCell;
}

ElementType Pop(LinkStack *S)
{
	// 删除并返回堆栈S的栈顶元素
	struct Node *FirstCell;
	ElementType TopElem;
	if(IsEmpty(S))
	{
		printf("堆栈空");
		return NULL;
	}
	else
	{
		FirstCell = S->Next;
		S->Next = FirstCell->Next;
		TopElem = FirstCell->Element;
		free(FirstCell);
		return TopElem;
	}
}

3、队列 

队列(Queue):具有一定操作约束的线性表

  插入和删除操作:只能在一端插入,而在另一端删除。

数据插入:入队列(AddQ);数据删除:出队列(DeleteQ);先来先服务;先进先出:FIFO

队列的主要操作:

  • Queue CreateQueue(int MaxSize):生成长度为MaxSize的空队列
  • int IsFullQ(Queue Q, int MaxSize):判断队列Q是否已满
  • void AddQ(Queue Q, ElementType item):将数据元素item插入队列Q中
  • int IsEmpty(Queue Q):判断队列Q是否为空
  • ElementType DeleteQ(Queue Q):将队头数据元素从队列中删除并返回

队列的顺序存储实现:

  队列的顺序存储结构通常由一个一维数组和一个记录队头元素位置的变量front和一个记录队尾元素位置的变量rear组成。

#define MaxSize <储存数据元素的最大个数>
typedef struct{
	ElementType Data[MaxSize];
	int rear;
	int front;
}Queue;

// 入队列
void AddQ(Queue *PtrQ, ElementType item)
{
	if((PtrQ->rear+1) % MaxSize == PtrQ->front)
	{
		printf("队列满");
		return;
	}
	PtrQ->rear = (PtrQ->rear+1) % MaxSize;
	PtrQ->Data[PtrQ->rear] = item;
}

// 入队列
ElementType DeleteQ(Queue *PtrQ)
{
	if(PtrQ->front == PtrQ->rear)
	{
		printf("队列空");
		return ERROR;
	}
	else
	{
		PtrQ->front = (PtrQ->front+1) % MaxSize;
		return PtrQ->Data[PtrQ->front];
	}
}

队列的链式存储实现:

  队列的链式存储结构也可以用一个单链表实现。插入和删除操作分别在链表的两头进行;值得思考的一个问题是:队列指针front和rear应该分别指向链表的哪一头?

typedef struct Node{
	ElementType Data;
	struct Node *Next;
}QNode;
typedef struct{ // 链队列结构
	QNode *rear; // 指向队尾结点
	QNode *front; // 指向队头结点
}LinkQueue;
LinkQueue *PtrQ;

// 不带头结点的链式队列出队操作的一个示例
ElementType DeleteQ(LinkQueue *PtrQ)
{
	QNode *FrontCell;
	ElementType FrontElem;

	if(PtrQ->front == NULL)
	{
		printf("队列空");
		return ERROR;
	}
	FrontCell = PtrQ->front;
	if(PtrQ->front == PtrQ->rear) // 若队列只有一个元素
		PtrQ->front = PtrQ->rear = NULL; // 删除后队列置空
	else
		PtrQ->front = PtrQ->front->Next;
	FrontElem = FrontCell->Data;
	free(FrontElem); // 释放被删除结点空间
	return FrontElem;
}

(本文完).

时间: 2024-10-11 05:00:51

数据结构第二讲:线性结构的相关文章

Mooc数据结构-基础和线性结构

1 数据结构 解决问题方法的效率,跟数据的组织方式有关 解决问题方法的效率,跟空间的利用效率有关 解决问题方法的效率,跟算法的巧妙程度有关 数据结构 数据对象在计算机中的组织方式 逻辑结构 物理存储结构 数据对象必定与一系列加在其上的操作相关联 完成这些操作所用的方法就是算法 抽象数据类型(Abstract Data Type) 数据类型 数据对象集 数据集合相关联的操作集 抽象: 描述数据类型的方法不依赖与具体实现 与存放数据的机器无关 与数据存储的物理结构无关 与实现操作的算法和编程语言无关

13. C#数据结构与算法 -- 线性结构

本文中,我们讨论了三个部分的内容: 什么是线性结构,线性结构有哪些特点 . 详细介绍了一个最简单线性结构顺序表,并且通过源代码进行一些的分析. 最后还举了一个例子,让我们更好的理解顺序表. 第一部分:什么是线性结构,线性结构有哪些特点 什么是线性结构,线性结构是最简单.最基本.最常用的数据结构.线性表是线性结构的抽象(Abstract), 线性结构的特点是结构中的数据元素之间存在一对一的线性关系. 这 种一对一的关系指的是数据元素之间的位置关系,即: (1)除第一个位置的数据元素外,其它数据元素

数据结构第二篇——线性表的顺序存储

?注:未经博主同意,不得转载. 线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素. 由于高级程序语言中的数组类型也有随机存取的特性,因此,通常都用数组来描述数据结构中的书序存储结构. 如下描述: 1 const int MaxListSize=100; //最大数据个数 2 //other way 3 //define MaxListSize 100 4 5 class List{ 6 7 private: 8 Data data[MaxListSize]; //存放数据

C语言 第二讲:分支结构

.BOOL 数据类型:只有真假两种状态 2.关系运算符:  >,>=,<,<=,==,!=; 判断,变量与变量之间,变量与常量之间的关系; 3.逻辑运算符:&&(逻辑与),||(逻辑或),!(逻辑非); 逻辑与 &&,并且 特点:同真为真,逻辑与两端的表达式同时为真的时候,整个逻辑与表达式结果为真; 逻辑或 ||,或者 特点:同假为假,逻辑或两端的表达式同时为假时,整个逻辑或表达式结果为假; 注意:逻辑与和逻辑或存在短路现象, 逻辑与短路现象:一旦发

数据结构第一讲,数据结构入门了解知识.

目录 数据结构入门简介 一丶数据结构的四种分类 1.集合结构 2.线性结构 3.树结构 4.图结构 二丶物理结构简介 1.存储器 2.数据元素的存储形式 三丶总结 数据结构入门简介 一丶数据结构的四种分类 我们常听的一句话就是, 数据结构 + 算法 = 程序 意思就是在我们的程序设计中,数据结构是必不可少的,那么什么是数据结构,数据结构简而言之就是针对数据关系而生产的产物.可能不是很理解.因为我们程序编写过程中,程序中产生的数据怎么存储这都是数据关系. 常见的数据结构种类. 集合 线性结构 树结

数据结构期末复习第二章线性表

第二章:线性表 1.线性结构的基本特征答:线性结构是若干数据元素构成的有序(次序)集① 集合中必存在唯一的一个“第一元素”:② 集合中必存在唯一的一个 “最后元素”:③ 除第一元素之外,均有 唯一的前驱:④ 除最后元素之外,均有 唯一的后继. 2.线性表的顺序存储结构和链式存储结构分别是______.(  D )   A. 顺序存取的存储结构.顺序存取的存储结构  B. 顺序存取的存储结构.随机存取的存储结构   C. 随机存取的存储结构.随机存取的存储结构  D. 随机存取的存储结构.顺序存取

数据结构之第二章线性表之静态链式存储

1--特点:用一维数组来描述线性表,用游标代替指针指示节点在数组中的相对位置.不设“指针”类型的高级语言中适用链表结构. 2--线性表的静态链式存储结构 ////  静态单链表.h//  单链表的静态存储//// 6 //  Copyright (c) 2014年 dashuai. All rights reserved.// #ifndef SLIST_H#define SLIST_H#include <stdio.h>#include <stdlib.h> #define MA

【军哥谈CI框架】之入门教程之第二讲:分析CI结构和CI是怎么工作的

[军哥谈CI框架]之入门教程之第二讲:分析CI结构和CI是怎么工作的 By hzp123   at 2014-02-18   265 阅读   0 回复   0.0 希赛币 之入门教程之第二讲:分析CI结构和CI是如何工作的大家好!上一节,我们共同部署了一个CI网站,做到这一点非常简单,但是,亲们,要知道这才刚刚开始额~~~ 这一节,我们主要来了解CI的文件结构和CI是如何工作的.这一点特别的重要! 一.CI的文件结构:了解CI的文件结构可以帮助我们快速的对CI框架有一个整体的认识,就好像我们去

数据结构1 线性结构

数据结构是指数据元素的结合及元素间的相互关系和构造方法.元素之间的相互关系是数据的逻辑结构,元素关系的存储形式成为存储结构.数据结构按照逻辑关系的不同分为线性结构和非线性结构两大类.其中线性结构是最基本的结构,元素顺序排列,常见的有线性表.栈.队列.数组.串. 一.线性表 1.线性表是最简单也是最常用的一种线性结构.一个线性表示n(n>=0)个元素的有限序列,非空线性表的特点为: 存在唯一的一个"第一个"元素: 存在唯一的一个"最后一个"元素: 除第一个元素外