数据结构(持续更新)

193 阅读10分钟

1.基本概念

  • 数据:数据是信息的载体,是描述客观事物属性的数,字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合
  • 数据元素:数据元素是数据的基本单位,通常作为一个整体进行考虑和处理
  • 数据项:一个元素可由若干个数据项组成,数据项是构成数据元素不可分割的最小单位
  • 数据结构:数据结构是数据元素相互之间存在一种或多种特定的关系的集合
  • 数据对象:具有相同性质的数据元素的集合,是数据的一个子集

2.数据结构的三要素

  • 逻辑结构:数据元素之间的逻辑关系是什么?

    • 集合:各个元素同属一个集合,别无其他关系

    • 线性结构:数据元素之间是一对一的关系。除了第一个元素,所有元素都有一个唯一前驱,除了最后一个元素,所有元素都有一个唯一后继。

    • 树形结构:数据元素之间是一对多关系

    • 图状结构(网状结构):数据元素之间是多对多关系

  • 物理结构(存储结构):如何用计算机表示数据元素之间的逻辑关系(简单来说就是逻辑关系的具体实现,逻辑关系是抽象的,类似于方法区,方法区的具体实现就是,永久代和元空间)

    • 顺序存储:把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现

    • 链式存储:逻辑上相邻的元素可以存储在物理位置上不相邻的存储单元中,借助指向元素存储地址的指针来表示元素之间的逻辑关系

    • 索引存储:在存储元素信息的同时,还建立附加的索引表。索引表中的每一项称为索引项,索引项的一般形式(关键字,地址)

    • 散列存储:根据元素的关键字直接计算出该元素的存储地址,又称哈希(Hash)存储

    • 总结:

      • 1.若采用顺序存储,则各个数据元素在物理上必须是连续的,若采用非顺序存储(链式,索引,散列),则各个数据元素在在物理上可以是离散的
      • 2.数据的存储结构会影响存储空间分配的方便程度——-(想把某个元素插入进指定位置)
      • 3.数据的存储结构会影响对数据运算的速度——(想取指定位置的某个元素)
  • 数据的运算:施加在数据上的运算,包括运算的定义实现运算的定义是针对逻辑结构的,指出运算的功能。运算的实现是针对存储结构的,指出运算的具体操作步骤。

算法

基本概念

  • 什么是算法

    • 程序 = 数据结构 + 算法
  • 算法必须具备的特性:

    • 有穷性:一个算法总在执行有穷步之后结束,且每一步都在有穷的时间内完成

      • 注:算法必须是有穷的,而程序可以是无穷的
    • 确定性:算法中每条指令必须有确切的含义,对于相同的输入,只能得出相同的输出

    • 可行性:算法中描述的操作都可以通过已经实现的基本运算执行有限次来实现。

    • 输入:一个算法可以有零个或多个输入,这些输入取自于某个特定的对象的集合(比如int类型,1,2,3)

    • 输出:一个算法有一个或多个输出,这些输出是与输入有着某种特定关系的量

  • ”好“算法的特质:

    • 正确性:能正确地解决问题
    • 可读性:对算法的描述能普遍让别人能看懂
    • 健壮性:算法能处理一些异常状况
    • 高效率和低存储量需求:
      • 算法执行省时,省内存
      • 时间复杂度低,空间复杂度低

线性表

  • 线性表的定义:线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为线性表的长度,及数据元素的个数,当n=0时线性表是一个空表
  • 线性表的基本操作:

顺序表

  • 顺序表的定义:用顺序存储的方式来实现线性表,顺序存储:把逻辑上相邻的元素在物理上也存储在相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。

  • 顺序表的实现——-静态分配

  • 顺序表的实现——-动态分配

  • 顺序表的基本操作——插入

  • 顺序表的基本操作——删除

  • 顺序表的基本操作——查找

    • 按位查找

    • 按值查找

以上操作代码实现

顺序表的实现-------静态分配
#include<stdio.h>
#define MaxSize 10//定义最大长度 
typedef struct {
	int	data[MaxSize];//用静态的"数组"存放数据元素 
	int length;//顺序表当前长度 
}SqList; //顺序表的类型定义

//基本操作-----初始化一个顺序表
void InitList(SqList &L){
	for(int i = 0; i < MaxSize; i++){
		L.data[i] = 0; //将所有数据元素设置为默认初始值 
		L.length = 0;  //顺序表初始长度为0 
	}
} 

int main(){
	SqList L;  //声明一个顺序表 
	InitList(L);//初始化顺序表 
	for(int i = 0; i<MaxSize; i++){
		printf("data[%d] = %d\n",i,L.data[i]);
	} 
	return 0;
}
顺序表的实现-------动态分配
#include<stdio.h>
#include<stdlib.h> 
#define InitSize 10//默认的最大长度
typedef struct{
	int* data;//指示动态分配数组的指针 
	int maxSize;//顺序表的最大容量 
	int length;//顺序表的当前长度 
}seqList;

void initList(seqList &L){
	//用malloc函数申请一片连续的存储空间
	L.data = (int*)malloc(sizeof(int) * InitSize);
	L.length = 0;
	L.maxSize = InitSize;
} 
//增加动态数组的长度
void increaseSize(seqList& L,int len){
	int* p = L.data;
	L.data = (int*)malloc(sizeof(int) * (L.maxSize + len));
	for(int i = 0; i < L.length; i++){
		L.data[i] = p[i];//将数据复制到新区域 
	}
	L.maxSize = L.maxSize + len;//顺序表最大长度增加len 
	free(p);  //释放原来的内存空间 
} 

int main(){
	seqList L;//声明一个顺序表
	initList(L); //初始化顺序表 
	increaseSize(L,5);
	printf("最大容量 = %d",L.maxSize); 
	return 0;
} 
顺序表的基本操作------插入
#include<stdio.h>
#define MaxSize 10//定义最大长度 
typedef struct {
	int	data[MaxSize];//用静态的"数组"存放数据元素 
	int length;//顺序表当前长度 
}SqList; //顺序表的类型定义

bool ListInsert(SqList& L,int i, int e){//在L的下标为i处插入元素e
	if(i < 0 || i >= L.length){//判断i的范围是否有效 
		return false;
	} 
	if(L.length >= MaxSize){//判断当前存储空间是否已满,不能插入。 
		return false;
	}
	
	for(int j = L.length - 1; j >= i; j-- ){//将下标>=i的元素从后依次向后移一位 
		L.data[j + 1] = L.data[j];
	} 
	L.data[i] = e;//在下标为i的位置放入元素e 
	L.length++;//长度加一 
}

void InitList(SqList &L){
	for(int i = 0; i < MaxSize; i++){
		L.data[i] = 0; //将所有数据元素设置为默认初始值 
		L.length = 0;  //顺序表初始长度为0 
	}
} 

int main(){
	SqList L;
	InitList(L);//初始化顺序表 
	//插入一些数据
	L.data[0] = 1;
	L.length++;
	L.data[1] = 2;
	L.length++;
	L.data[2] = 3;
	L.length++;
	L.data[3] = 4;
	L.length++;
	ListInsert(L,1,5);
	for(int i = 0; i < L.length;i++){
		printf("data[%d] = %d\n",i,L.data[i]);
	}
	return 0;
} 
顺序表的基本操作------删除
#include<stdio.h>
#define MaxSize 10//定义最大长度 
typedef struct {
	int	data[MaxSize];//用静态的"数组"存放数据元素 
	int length;//顺序表当前长度 
}SqList; //顺序表的类型定义

bool ListDelete(SqList& L,int i,int& e){
	if(i < 0 || i >= L.length){//判断i的范围是否有效 
		return false;
	} 
	e = L.data[i]; //将被删除的元素赋给e
	for(int j = i + 1; j <= L.length - 1; j++){
		L.data[j - 1] = L.data[j];
	} 
	L.length--;
	return true;
} 

void InitList(SqList &L){
	for(int i = 0; i < MaxSize; i++){
		L.data[i] = 0; //将所有数据元素设置为默认初始值 
		L.length = 0;  //顺序表初始长度为0 
	}
} 

int main(){
	SqList L;
	InitList(L);//初始化顺序表 
	//插入一些数据
	L.data[0] = 1;
	L.length++;
	L.data[1] = 2;
	L.length++;
	L.data[2] = 3;
	L.length++;
	L.data[3] = 4;
	L.length++;
	int e = -1;
	ListDelete(L,0,e);
	for(int i = 0; i < L.length;i++){
		printf("data[%d] = %d\n",i,L.data[i]);
	}
	return 0;
} 
顺序表的基本操作------按位查找
#include<stdio.h>
#include<stdlib.h> 
#define InitSize 10//默认的最大长度
typedef struct{
	int* data;//指示动态分配数组的指针 
	int maxSize;//顺序表的最大容量 
	int length;//顺序表的当前长度 
}seqList;

void initList(seqList &L){
	//用malloc函数申请一片连续的存储空间
	L.data = (int*)malloc(sizeof(int) * InitSize);
	L.length = 0;
	L.maxSize = InitSize;
} 

int getElem(seqList L,int i){
	return L.data[i];
}

int main(){
	seqList L;
	initList(L);//初始化顺序表 
	//插入一些数据
	L.data[0] = 1;
	L.length++;
	L.data[1] = 2;
	L.length++;
	L.data[2] = 3;
	L.length++;
	L.data[3] = 4;
	L.length++;
	int elem = getElem(L,1);
	printf("elem = %d",elem);
	return 0;
} 
顺序表的基本操作------按值查找
#include<stdio.h>
#include<stdlib.h> 
#define InitSize 10//默认的最大长度
typedef struct{
	int* data;//指示动态分配数组的指针 
	int maxSize;//顺序表的最大容量 
	int length;//顺序表的当前长度 
}seqList;

void initList(seqList &L){
	//用malloc函数申请一片连续的存储空间
	L.data = (int*)malloc(sizeof(int) * InitSize);
	L.length = 0;
	L.maxSize = InitSize;
} 

int getElem(seqList L,int i){
	for(int j = 0; j < L.length; j++){
		if(L.data[j] == i){
			return j;
		}
	}
	return -1;

}

int main(){
	seqList L;
	initList(L);//初始化顺序表 
	//插入一些数据
	L.data[0] = 1;
	L.length++;
	L.data[1] = 2;
	L.length++;
	L.data[2] = 3;
	L.length++;
	L.data[3] = 4;
	L.length++;
	int elem = getElem(L,4);
	printf("elem = %d",elem);
	return 0;
} 

链表

  • 单链表

    • 定义(代码的实现)
      • 带头节点
      • 不带头节点
    • 基本操作的实现
      • 插入
        • 按位插入
          • 带头节点
          • 不带头节点
        • 指定节点的插入(带头结点)
          • 后插操作
          • 前插操作
      • 删除
        • 按位删除(带头结点)
        • 指定节点的删除(带头节点)
    • 查找(带头结点)
      • 按位查找
      • 按值查找
    • 单链表的建立(带头结点)
      • 头插法
      • 尾插法
  • 双链表

  • 循环链表

  • 静态链表

以上操作代码实现

单链表带头节点定义
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表节点类型 
	int i; 			  //每个节点存放一个数据元素 
	LNode * next;     //指向下一个节点 
}LNode,*LinkList;

//初始化一个单链表(带头节点)
bool InitList(LinkList &L){
	L = (LNode*)malloc(sizeof(LNode)); //分配一个头结点 
	if(L == NULL){				//内存不足分配失败 
		return false;			
	}
	L->next = NULL;				//头节点之后暂时还没有节点 
	return true;
} 
int main(){
	LinkList L;  //声明一个指向单链表的指针 
	InitList(L);//初始化 
	return 0;
}
单链表无头节点定义
#include<stdio.h>
typedef struct LNode{ //定义单链表节点类型 
	int i; 			  //每个节点存放一个数据元素 
	LNode * next;     //指向下一个节点 
}LNode,*LinkList;


//初始化一个空的单链表 (无头结点) 
bool InitList(LinkList &L){
	L = NULL;		//空表,暂时还没有任何结点 
	return true;
}

int main(){
	LinkList L;  //声明一个指向单链表的指针 
	InitList(L);//初始化 
	return 0;
}
单链表带头节点的按位插入
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表节点类型 
	int data; 			  //每个节点存放一个数据元素 
	LNode * next;     //指向下一个节点 
}LNode,*LinkList;

//在第i个位置插入元素e(带头节点) 
bool ListInsert(LinkList &L,int i,int e){
	//判断位置是否是合法位置 
	if(i < 1){
		return false;
	}
	LNode * p; //定义指针p指向当前指向的节点
	int j = 0; //当前指针p指向的是第几个节点
	p = L; //L指向头结点,头结点是第0个节点,不存数据。赋给p
	while(p != NULL && j < i-1){   //循环找到 第 i-1 个节点 
		p = p->next;
		j++; 
	} 
	if(p == NULL){  //i值不合法 
		return false;
	}
	LNode * s = (LNode *)malloc(sizeof(LNode));//给第i个节点分配一个空间 
	s->next = p->next;		//将 i-1 节点中下一个节点的 地址赋给第i个节点的下一个节点 
	p->next = s;				//将第i个节点的地址 赋给第i-1个节点中的next 
	s->data = e; 				//把值给第i个节点的data 
	return true; 
} 
单链表无头节点的按位插入
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表节点类型 
	int data; 			  //每个节点存放一个数据元素 
	LNode * next;     //指向下一个节点 
}LNode,*LinkList;

//在第i个位置插入元素e(无头节点) 
bool ListInsert(LinkList &L,int i,int e){
	//判断位置是否是合法位置 
	if(i < 1){
		return false;
	}
	if(i == 1){
		LNode * s = (LNode *)malloc(sizeof(LNode));//给第1个节点分配一个空间 
		s->data = e;
		s->next = NULL;
		L = s;
		return true;
	} 
	LNode * p; //定义指针p指向当前指向的节点
	int j = 1; //当前指针p指向的是第几个节点
	p = L; //L指向头结点,头结点是第0个节点,不存数据。赋给p
	while(p != NULL && j < i-1){   //循环找到 第 i-1 个节点 
		p = p->next;
		j++; 
	} 
	if(p == NULL){  //i值不合法 
		return false;
	}
	LNode * s = (LNode *)malloc(sizeof(LNode));//给第i个节点分配一个空间 
	s->next = p->next;		//将 i-1 节点中下一个节点的 地址赋给第i个节点的下一个节点 
	p->next = s;				//将第i个节点的地址 赋给第i-1个节点中的next 
	s->data = e; 				//把值给第i个节点的data 
	return true; 
} 
int main(){
	LinkList L;
	L = (LNode*)malloc(sizeof(LNode));
	ListInsert(L,1,2);
	printf("L[0] = %d",L->data);
	return 0;
}
单链表指定节点的后插操作
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表节点类型 
	int data; 			  //每个节点存放一个数据元素 
	LNode * next;     //指向下一个节点 
}LNode,*LinkList;

//后插操作:在p节点之后插入元素e
bool InsertNextNode(LNode * p,int e){
	if(p == NULL){
		return false;
	}
	LNode * s = (LNode*)malloc(sizeof(LNode));
	if(s == NULL){
		return false;
	}
	s->next = p->next;
	p->next = s;				
	s->data = e;
	return true; 
} 

int main(){
	LinkList L;
	L = (LNode*)malloc(sizeof(LNode));
	L->data = 1;
	InsertNextNode(L,2);
	printf("L[1] = %d",L->next->data);
	return 0;
} 
单链表指定节点的前插操作
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表节点类型 
	int data; 			  //每个节点存放一个数据元素 
	LNode * next;     //指向下一个节点 
}LNode,*LinkList;
//前插操作,在p节点之前插入元素e

bool InsertPriorNode(LNode * p,int e){
	if(p == NULL){
		return false;
	}
	LNode * s = (LNode*)malloc(sizeof(LNode));
	if(s == NULL){
		return false;
	}
	s->next = p->next;
	s->data = p->data;//将p中的元素给新节点s 
	p->next = s;      //将新节点加在p节点后 
	p->data = e;	  //将e元素给p节点 
	return true; 
} 
单链表中按位删除
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表节点类型 
	int data; 			  //每个节点存放一个数据元素 
	LNode * next;     //指向下一个节点 
}LNode,*LinkList;

bool ListDelete(LinkList &L,int i,int &e){
	if(i < 1){
		return false;
	}
	LNode * p; //指针p指向当前扫描的节点
	int j = 0; //当前节点为第几个
	p = L;
	while(p != NULL && j < i-1){
		p = p->next;
		j++;
	} 
	if(p == NULL){
		return false;
	}
	e = p->next->data;//用e返回元素的值 
	p->next = p->next->next; 
	free(p->next);//释放第i个节点 
	return true;
} 
单链表中指定节点删除
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表节点类型 
	int data; 			  //每个节点存放一个数据元素 
	LNode * next;     //指向下一个节点 
}LNode,*LinkList;

//指定节点的删除
bool DeleteNode(LNode * p){
	if(p == NULL){
		return false;
	}
	LNode * s = p->next;//领s指向p的后继节点 
	p->data = s->data;//把p的后继节点的数据赋给p节点 
	p->next = s->next;//把p的后继节点的后继节点的地址赋给p的next 
	free(s); //释放p的后继节点 
	return true; 
}

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 21 天,点击查看活动详情