C__数据结构和算法__

1.什么是数据结构

数据结构是研究非数值计算的,主要解决程序设计过程中对象操作的问题,以及对象之间的关系和操作的相关问题,即对象之间的关系和对象的操作。

1、数据结构的起源

起源于1968年,美国的高纳德教授开设一门《基本算法》的课程,这门课程开创了数据结构课程体系的先河。

2、程序设计 = 数据结构 + 算法

Pascal之父——Nicklaus Wirth,凭借这个公式获得了1984的图灵奖。

2.数据结构的基本概念

1.数据:所有能输入到计算机中用来描述客观事物的符号。
2.数据项:有独立含义的数据的最小单位,也叫作域。
3.数据元素:数据的基本单位,也称为节点。
4.数据结构:若干数据元素组成的集合。
5.算法:狭义的算法指的是一种数据结构能够具有的操作,广义上的算法指的是对特定问题求解步骤的描述。

3.数据结构的三个方面

1.数据的逻辑结构
2.数据的存储结构
3.数据结构的运算

4.四个基本的逻辑结构

1.集合结构:元素之间除了同属一个区间外没有任何关系,如下图:在这里插入图片描述
2.线型结构:元素之间存在一对一关系,如:
在这里插入图片描述
3.树型结构:元素之间存在一对多关系,如:
在这里插入图片描述
4.图型结构:元素之间存在多对多关系,如:
在这里插入图片描述

5.数据结构的存储方式

1.顺序存储:元素之间是连续的,使用的是一段连续的内存空间,用数据元素在内存的相对位置来表示数据之间的关系
优点:能够随机访问,访问效率、速度极高。
缺点:空间的利用率比较低(对内存的要求高,必须要整块的内存),元素的增删不方便。
在这里插入图片描述
2.链式存储:元素之间是分散的,每个元素存储在彼此独立的内存空间,每个元素中有一个指针记录了下一个元素的内存首地址。
优点:插入、删除方便,空间的利用率高(对内存的要求低,因为使用指针指向下一个数据,所以可以分散储存在内存中)
缺点:不能随机访问,只能逐个遍历,访问效率、速度低。
在这里插入图片描述

6.数据结构的运算

1.建立一个数据结构:create
2.销毁一个数据结构:destory
3.向数据结构中添加一个元素:addend
4.向数据结构中插入一个元素:insert
5.从数据结构中删除一个元素:delete
6.修改数据结构中某个元素:modify
7.查找数据结构中某个元素:find
8.访问数据结构中某个元素:access
9.对数据结构进行排序:sort
10.遍历数据结构:show

7.顺序表和链式表的实现

1顺序表:

元素之间具有一对一关系,但采用顺序结构存储,如下:

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

#define TYPE int

// 设计数据结构
typedef struct Array
{
	TYPE* base;
	// 数组大小
	size_t size;
	// 数组里的数据个数
	size_t count;
}Array;

// 创建数组
Array* create_array(size_t size)
{
	Array* array = malloc(sizeof(Array));
	array->base = malloc(sizeof(TYPE)*size);
	array->size = size;
	array->count = 0;
	return array;
}

// 销毁数组
void destory_array(Array* array)
{
	free(array->base);
	free(array);
}

// 添加一个元素
bool addend_array(Array* array,TYPE data)
{
	if(array->count >= array->size)
		return false;

	array->base[array->count++] = data;
	return true;
}

// 插入一个元素
bool insert_array(Array* array,size_t index,TYPE data)
{
	// 判断是否有空位置
	if(array->count >= array->size)
		return false;
	// 检查下标是否正确
	if(index >= array->count)
		return false;
	
	// 移动数据
	for(int i=array->count++; i>index; i--)
	{
		array->base[i] = array->base[i-1];
	}

	// 存储到数组中
	array->base[index] = data;
	
	return true;
}

// 删除一个元素
bool delete_array(Array* array,size_t index)
{
	if(index >= array->count)
		return false;
	
	for(int i=index; i<array->count-1; i++)
	{
		array->base[i] = array->base[i+1];
	}

	array->count--;

	return true;
}

// 查找元素
int find_array(Array* array,TYPE data)
{
	for(int i=0; i<array->count; i++)
	{
		if(data == array->base[i])
			return i;
	}

	return -1;
}

// 访问元素
TYPE* access_array(Array* array,size_t index)
{
	if(index >= array->count)
		return NULL;

	return array->base+index;
}

// 修改指定位置元素的值
bool modify_array(Array* array,size_t index,TYPE data)
{
	if(index >= array->count)
		return false;
	
	array->base[index] = data;
	return true;
}

// 对数组排序
void sort_array(Array* array)
{
	for(int i=0; i<array->count-1; i++)
	{
		for(int j=i+1; j<array->count; j++)
		{
			if(array->base[i] > array->base[j])
			{
				TYPE temp = array->base[i];
				array->base[i] = array->base[j];
				array->base[j] = temp;
			}
		}
	}
}

// 遍历数组
void show_array(Array* array)
{
	for(int i=0; i<array->count; i++)
	{
		printf("%d ",array->base[i]);
	}
	printf("\n");
}
2链式表:

表结构的链式存储,元素之间存在一对一关系,元素的逻辑次序和物理次序可能不一致(在内存中是分散存储元素)。
为了表示元素之间的逻辑关系,需要每个元素中有一个数据项(指针)记录下一元素的位置(地址)。(单向链表)

我的实现方法是双向链表,在单向链表的基础上,为每个数据项额外加了一个记录上一个元素位置的指针,如下:

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

#define TYPE int

// 设计链表的元素
typedef struct Node
{
	TYPE data;
	struct Node* next;
}Node;

// 设计链表
typedef struct List
{
	Node* head;
	Node* tail;
	size_t count;
}List;

// 创建元素
Node* create_node(TYPE data)
{
	Node* node = malloc(sizeof(Node));
	node->data = data;
	node->next = NULL;
	return node;
}

// 创建链表
List* create_list(void)
{
	List* list = malloc(sizeof(List));
	list->head = NULL;
	list->tail = NULL;
	list->count = 0;
	return list;
}

// 销毁表
void destory_list(List* list)
{
	while(list->count)
	{
		Node* node = list->head;
		list->head = node->next;
		free(node);
		list->count--;
	}
	free(list);
}

// 判空
bool empty_list(List* list)
{
	return !list->count;
}
// 添加
void addend_list(List* list,TYPE data)
{
	Node* node = create_node(data);

	// 判断链表是否为空
	if(empty_list(list))
	{
		list->head = node;
		list->tail = node;
	}
	else
	{
		list->tail->next = node;
		list->tail = node;
	}

	list->count++;
}

// 插入
bool insert_list(List* list,size_t index,TYPE data)
{
	if(index >= list->count)
		return false;
	
	Node* newn = create_node(data);
	if(0 == index)
	{
		newn->next = list->head;
		list->head = newn;
	}
	else
	{
		Node* node = list->head;
		for(int i=0; i<index-1; i++)
			node = node->next;
		newn->next = node->next;
		node->next = newn;
	}
	list->count++;
	return true;
}

// 查找
int find_list(List* list,TYPE data)
{
	Node* node = list->head;
	for(int i=0; i<list->count; i++)
	{
		if(node->data == data)
			return i;
		node = node->next;
	}

	return -1;
}


// 删除-接位置
bool delete_index_list(List* list,size_t index)
{
	if(index >= list->count)
		return false;
	
	Node* temp = list->head;
	if(0 == index)
		list->head = temp->next;
	else
	{
		Node* node = list->head;
		for(int i=0; i<index-1; i++)
			node = node->next;
		temp = node->next;
		node->next = temp->next;
	}
	list->count--;
	free(temp);
	return true;
}
// 删除-按值
bool delete_value_list(List* list,TYPE data)
{
	int index = 0 , flag = false;
	while((index = find_list(list,data))!=-1)
	{
		delete_index_list(list,index);
		flag = true; 
	}
	return flag;
}

// 修改-按值
bool modify_value_list(List* list,TYPE old,TYPE new)
{
	bool flag = false;
	for(Node* node=list->head; node; node=node->next)
	{
		if(old == node->data)
		{
			node->data = new;
			flag = true;
		}
	}
	return flag;
}
// 修改-按位置
bool modify_index_list(List* list,size_t index,TYPE data)
{
	if(index >= list->count)
		return false;

	Node* node = list->head;
	for(int i=0; i<index; i++,node=node->next);
	node->data = data;
	return true;
}

// 排序
void sort_list(List* list)
{
	for(Node* i=list->head; i->next; i=i->next)
	{
		for(Node* j=i->next; j; j=j->next)
		{
			if(i->data < j->data)
			{
				TYPE temp = i->data;
				i->data = j->data;
				j->data = temp;
			}
		}
	}
}

// 遍历
void show_list(List* list)
{
	for(Node* node=list->head; node; node=node->next)
	{
		printf("%d ",node->data);
	}
	printf("\n");
}

8.栈和队列

栈和队列是两种应用非常广泛的数据结构,它们都是特殊的线性结构,操作受限的线性表。
栈(也叫堆栈)
基本特征:只有一个数据出入口,数据先进的,只能后出
基本操作:压栈push、弹栈pop。

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

#define TYPE int

typedef struct Node
{
	TYPE data;
	struct Node* next;
}Node;

typedef struct StackList
{
	Node* top;
	size_t cnt;
}StackList;

// 创建结点
Node* create_node(TYPE data)
{
	Node* node = malloc(sizeof(Node));
	node->data = data;
	node->next = NULL;
	return node;
}

// 创建栈
StackList* create_stack_list(void)
{
	StackList* stack = malloc(sizeof(StackList));
	stack->top = NULL;
	stack->cnt = 0;
	return stack;
}

// 栈空
bool empty_stack_list(StackList* stack)
{
	return !stack->cnt;
}

// 入栈
void push_stack_list(StackList* stack,TYPE data)
{
	Node* node = create_node(data);
	node->next = stack->top;
	stack->top = node;
	stack->cnt++;
}

// 出栈
bool pop_stack_list(StackList* stack)
{
	if(empty_stack_list(stack))
		return false;
	Node* node = stack->top;
	stack->top = node->next;
	stack->cnt--;
	free(node);
	return true;
}

// 栈顶
Node* top_stack_list(StackList* stack)
{
	return stack->top;
}

// 销毁栈
void destory_stack_list(StackList* stack)
{
	while(!empty_stack_list(stack))
	{
		pop_stack_list(stack);
	}
	free(stack);
}

队列
基本特征:是一种先进先出的线性表,只能在一端插入数据(队尾),另一端删除数据(队头),共两个控制数据的端口。
基本操作:入队、出队、查看队头、查看队尾

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

#define TYPE int

typedef struct Node
{
	TYPE data;
	struct Node* next;
}Node;

typedef struct QueueList
{
	Node* head;
	Node* tail;
	size_t cnt;
}QueueList;

// 创建结点
Node* create_node(TYPE data)
{
	Node* node = malloc(sizeof(Node));
	node->data = data;
	node->next = NULL;
	return node;
}

// 创建队列
QueueList* create_queue_list(void)
{
	QueueList* queue = malloc(sizeof(QueueList));
	queue->head = NULL;
	queue->tail = NULL;
	queue->cnt = 0;
	return queue;
}

// 队空
bool empty_queue_list(QueueList* queue)
{
	return !queue->cnt;
}

// 入队
void push_queue_list(QueueList* queue,TYPE data)
{
	Node* node = create_node(data);
	if(empty_queue_list(queue))
	{
		queue->head = node;
		queue->tail = node;
	}
	else
	{
		queue->tail->next = node;
		queue->tail = node;
	}
	queue->cnt++;
}

// 出队
bool pop_queue_list(QueueList* queue)
{
	if(empty_queue_list(queue))
		return false;
	Node* node = queue->head;
	queue->head = node->next;
	queue->cnt--;
	free(node);
	return true;
}

// 队头
Node* head_queue_list(QueueList* queue)
{
	if(empty_queue_list(queue))
		return NULL;
	return queue->head;
}

// 队尾
Node* tail_queue_list(QueueList* queue)
{
	if(empty_queue_list(queue))
		return NULL;
	return queue->tail;
}

// 销毁队列
void destory_queue_list(QueueList* queue)
{
	while(!empty_queue_list(queue))
	{
		pop_queue_list(queue);
	}
	free(queue);
}

9.递归

递归是一种行为(函数自己调用自己)而不是一种算法,它是分治、递推等算法的一种实现(凡是循环能解决的问题,递归都能解决)。
递归有一个很大的缺点就是耗费资源(每调用一次,就要分配一块函数所需要的栈空间),因此能用循环解决的问题,坚决不用递归
递归很容易陷入死循环,因此写递归函数首先要考虑的就是出口问题
递归适合解决什么问题:
1.复杂问题,由于问题太过于复杂,无法写出具体的算法(河内之塔、全排列、组合),思路是把复杂的问题分解成若干个相同的小问题,每次调用解决一个小问题,把剩余的问题交给下次调用,直到问题解决(到达出口)。
2.非线性问题,发散型的问题、或者数据结构比较适合使用递归的问题

void func()
{
	// 判断是否到达出口
	// 解决一部分问题
	// 自己调用自己,把剩余的问题交给下次调用
}

注意:使用递归解决问题时,只要思路正确即可,不要模拟它的运行过程。

10.树

树形结构:

元素之间存在一对多关系。
一棵树(tree)是由n(n >0)个元素组成的有限集合,其中:
每个元素称为结点(node);
有一个特定的结点,称为根结点或根(root);
除根结点外,其余结点被分成m(m >= 0)个互不相交的有限集合,而每个子集又都是一棵树(称为原树的子树)。

二叉树:

最多只有两个子结点的树叫做二叉树。

完全二叉树:

若设二叉树的高度为h,除第h层外,其它各层(1 ~ h-1)的结点树都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树(上一层放满,才放下一层,从左到右依次使用)。

满二叉树:

除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。

有序二叉树:

左子叶都小于它的根,右子叶都大等于它的二叉树。

平衡二叉树:

它是一颗有序二叉树,且具有以下性质:它是一颗空树或它的左右两个子树的高度相差不超过1,并且左右两个子树都是一颗平衡二叉树。

二叉树相关术语:

树的结点(node):包含一个数据元素以及若干个指向子树的分支;
孩子结点(child node):结点的子树的根称为该结点的孩子;
双亲结点:B结点是A结点的孩子,则A结点是B结点的双亲;
兄弟结点:同一双亲的孩子结点;
祖先结点:从根到该节点上,所经过的分支上的所有结点都是该结点的祖先结点;
子孙结点:以某结点为根的子树中,任一结点都称为该结点的子孙;
结点层:根结点的层定义为1,根的孩子为第二层结点,依此类推;
树的深度:树中最大的结点层数;
结点的度:结点子树的个数;
树的度:树中最大的结点度;
叶子结点:也叫终端结点,是度为0的结点;
分支结点:度不为0的结点;
森林:指若干棵互不相交的树的集合;
路径:对于一棵子树中的任意两个不同的结点,如果从一个结点出发,按层次自上而下沿着一个个树枝能到达另一结点,称它们之间存在着一条路径。可用路径所经过的结点序列表示路径,路径的长度等于路径上的结点个数减1。

树的遍历:

1.前序遍历:根 左 右;
2.中序遍历:左 根 右;
3.后序遍历:左 右 根;
4.层序遍历:从上到下;

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

#define Type char

typedef struct TreeNode
{
	Type data;
	struct TreeNode* left;
	struct TreeNode* right;
}TreeNode;

TreeNode* create_TreeNode(Type data)
{
	TreeNode* TreeNode = malloc(sizeof(TreeNode));
	TreeNode->data = data;
	TreeNode->left = NULL;
	TreeNode->right = NULL;
	return TreeNode;
}
// 前序
void prev_show(TreeNode* tree)
{
	if(NULL == tree)
	{
		printf("# ");
		return;
	}

	printf("%c ",tree->data);
	prev_show(tree->left);
	prev_show(tree->right);
}

// 中序
void in_show(TreeNode* tree)
{
	if(NULL == tree)
	{
		printf("# ");
		return;
	}

	in_show(tree->left);
	printf("%c ",tree->data);
	in_show(tree->right);
}

// 后序
void post_show(TreeNode* tree)
{
	if(NULL == tree)
	{
		printf("# ");
		return;
	}

	post_show(tree->left);
	post_show(tree->right);
	printf("%c ",tree->data);
}

// 层序
void print_show(TreeNode* tree)
{
	QueueList* queue = create_queue_list();
	push_queue_list(queue,tree);
	while(!empty_queue_list(queue))
	{
		TreeNode* node = head_queue_list(queue)->data;
		printf("%c ",node->data);
		if(node->left)push_queue_list(queue,node->left);
		if(node->right)push_queue_list(queue,node->right);
		pop_queue_list(queue);
	}
}

11.图

图是一种多对多的数据结构,是一种使用非常广泛的数据结构

分类

有向图:点与点之间是单向连通。(A可到B,B到不了A)
无向图:点与点之间是双向连通。(A可到B,B也可到A)

构成

顶点:可以被连接的元素。
:连接元素的通道。

表示

有向图:< 1,2 > < 2,1 > < 3,2 > < 3,1 >
无向图:(1,2) (2,3) (3,1)

术语

完全图:是指任意两个结点之间都有一个边相连,也就是结点两两相连;
连通图:是指任意两个结点之间都有一个路径相连。

存储

邻接矩阵:使用二位数组来表示图的连接关系。
邻接表:表头+表元素来表示图的连接关系

遍历

深度优先(DFS)
1.访问顶点v;
2.依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相同的顶点都被访问;
3.若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止,当然,当人们刚刚掌握深度优先搜索的时候,常常用它来走迷宫,事实上我们还有别的方法,那就是广度优先搜索(BFS)
广度优先(BFS)
1.从图中某个顶点v0出发,并访问此顶点;
2.从v0出发,访问v0的各个未曾访问过的邻接点w1,w2,…,wk;然后依次从w1,w2,…,wk出发访问各自未被访问的邻接点;
3.重复步骤2,直到全部顶点都被访问位置

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

// 表结点
typedef struct Node
{
	size_t index;
	struct Node* next;
}Node;

// 表头
typedef struct List
{
	char ch;//顶点
	Node* head;
}List;

// 图
typedef struct Graph
{
	List* list;
	size_t vertex;
	size_t edge;
}Graph;

Node* create_node(size_t index)
{
	Node* node = malloc(sizeof(Node));
	node->index = index;
	node->next = NULL;
	return node;
}

// 创建表
Graph* create_graph(char* str)
{
	Graph* graph = malloc(sizeof(Graph));
	graph->vertex = strlen(str);
	graph->edge = 0;

	graph->list = malloc(sizeof(List)*graph->vertex);
	for(int i=0; i<graph->vertex; i++)
	{
		graph->list[i].ch = str[i];
		graph->list[i].head = NULL;
	}
	return graph;
}

// 添加边
bool addend_graph(Graph* graph,size_t start,size_t end)
{
	if(start >= graph->vertex || end >= graph->vertex)
		return false;
	
	Node* node = create_node(end);
	node->next = graph->list[start].head;
	graph->list[start].head = node;
	graph->edge++;

	return true;
}


void dfs_show(Graph* graph,size_t start,char* access)
{
	printf("%c ",graph->list[start].ch);
	access[start] = 1;

	Node* node = graph->list[start].head;
	while(node)
	{
		if(!access[node->index])
			dfs_show(graph,node->index,access);
		node = node->next;
	}
}

// 深度优先遍历
void dfs_show_graph(Graph* graph)
{
	char access[graph->vertex];
	for(int i=0; i<graph->vertex; i++)
	{
		access[i] = 0;
	}
	
	for(int i=0; i<graph->vertex; i++)
	{
		if(!access[i])
			dfs_show(graph,i,access);
	}
}

void bfs_show(Graph* graph,size_t start,char* access)
{
	printf("%c ",graph->list[start].ch);
	access[start] = 1;
	char queue[graph->vertex];
	size_t head = 0 , tail = 0;
	queue[tail++] = start;

	while(head < tail)
	{
		size_t start = queue[head++];
		Node* node = graph->list[start].head;
		while(node)
		{
			if(!access[node->index])
			{
				printf("%c ",graph->list[node->index].ch);
				access[node->index] = 1;
				queue[tail++] = node->index;
			}
			node = node->next;
		}
	}
}

// 广度优先遍历
void bfs_show_graph(Graph* graph)
{
	char access[graph->vertex];
	for(int i=0; i<graph->vertex; i++)
	{
		access[i] = 0;
	}
	
	for(int i=0; i<graph->vertex; i++)
	{
		if(!access[i])
			bfs_show(graph,i,access);
	}
	
}

12.查找

顺序查找:从头到尾逐个比较。
优点:对数据没有要求,算法稳定
缺点:效率低,不适合大规模数据的查找
二分查找:数据必须是有序的,然后与中间值比较,比中间大则向右半部分查找,比中间值小则向左半部分查找。
优先:效率极高
缺点:数据必须先进行排序
块查找:类似于查找英文词典

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

// 顺序查找
int ordef_find(int arr[],size_t len,int key)
{
	for(int i=0; i<len; i++)
	{
		if(arr[i] == key)
			return i;
	}
	return -1;
}

// 二分查找-数据有序
int _binary_find(int arr[],int left,int right,int key)
{
	if(left >= right) return -1;
	int p = (left+right)/2;
	if(arr[p] == key) return p;
	if(arr[p] > key)
		return _binary_find(arr,left,p,key);
	else
		return _binary_find(arr,p+1,right,key);
}

int binary_find(int arr[],size_t len,int key)
{
	return _binary_find(arr,0,len,key);
}

// 使用循环实现二分查找
/*int binary_find(int arr[],size_t len,int key)
{
	int left = 0 , right = len;
	while(left < right)
	{
		int p = (left + right) / 2;
		if(arr[p] == key) return p;
		if(arr[p] > key)
			right = p;
		else 
			left = p+1;
	}
	return -1;
}*/

13.排序

排序算法有很多,下面列举几个常用的算法
冒泡排序:对数据的有序性是很敏感的,一旦排序完成会立即停止,如果待排序的数据是基本有序的,它的排序效率是非常高的;冒泡也是实现最简单的排序,代码不易出错,安全性高。

// 冒泡排序
void bubble_sort(int arr[],size_t len)
{
	for(int i=1; i<len; i++)
	{
		bool flag = true;
		for(int j=0; j<len-i; j++)
		{
			printf("*");
			if(arr[j] > arr[j+1])
			{
				flag = false;
				swap(arr[j],arr[j+1]);
			}
		}
		// 判断是否有数据交换,如果没有,则代表排序完成,直接返回
		if(flag) return;
	}
}

插入排序:在已经排序好的数据中插入新的数据,合适用此方法排序。

// 插入排序
void insert_sort(int arr[],int end,int count)
{
	for(int i=end+1,j; i<end+count; i++)
	{
		int val = arr[i];
		for(j=i; j>=0; j--)
		{
			if(arr[j-1] > val)
				arr[j] = arr[j-1]; 
			else
				break;
		}
		arr[j] = val;
	}
}

选择排序:是冒泡排序的变种,不是正统的排序方式,但代码简单。

// 选择排序
void select_sort(int arr[],size_t len)
{
	for(int i=0; i<len-1; i++)
	{
		int min = i;
		for(int j=i+1; j<len; j++)
		{
			if(arr[min] > arr[j])
				min = j;
		}
		if(min != i)
			swap(arr[min],arr[i]);
	}
}

快速排序:在所有排序算法中,平均速度最快的一种

void _quick_sort(int arr[],size_t left,size_t right)
{
	// 计算标杆的位置
	int p = (left+right)/2;
	// 备份标杆的值
	int val = arr[p];

	// 备份起点和终点
	size_t l=left,r=right-1;
	// 循环保证起点和终点不相遇
	while(l<r)
	{
		// 在左边找出比标杆大的值
		for(;l<p && arr[l]<=val; l++);
		// 如果循环不是以l<p结束的说明找到大于标杆的值
		if(l<p)
		{
			// 把大于标杆的值移动到标杆位置
			arr[p] = arr[l];
			// 这个数的位置就成了新的标杆位置
			p = l;
		}
		// 大右边找到比标杆小的值
		for(;p<r && arr[r]>val; r--);
		// 如果循环不是以p<r结束的说明撕开小于标杆的值
		if(p<r)
		{
			// 把小于标杆的值值移动到标杆位置
			arr[p] = arr[r];
			// 这个数的位置就成了新的标杆位置
			p = r;
		}
	}
	// 还原标杆值
	arr[p] = val;

	// 排序标杆左边数据
	if(p-left > 1) _quick_sort(arr,left,p);
	// 排序标杆右边的数据
	if(right-p > 2) _quick_sort(arr,p+1,right);
}

归并排序:把待排序的数据拆分成不可再拆分的数据对,再1:1合并再一起,形成2:2,然后再把2:2合并再一起形成4:4,直到数组排序完成。

// 将拆分后的数据排序
void merge(int arr[],int temp[],size_t left,size_t p,size_t right)
{
	int i=left,j=p+1,k=left;
	// 只要有一个块的数据排完了,就退出循环
	while(i<=p && j<=right)
	{
		if(arr[i] < arr[j])
			temp[k++] = arr[i++];
		else
			temp[k++] = arr[j++];
	}
	// 判断左边数据是否排完
	while(i<=p) temp[k++] = arr[i++];
	// 判断右边数据是否排完
	while(j<=right) temp[k++] = arr[j++]; 
	
	// 将排好的数组放入arr中
	for(int i=left; i<=right; i++)
	{
		arr[i] = temp[i];
	}
}
// 将数组拆分
void _merge_sort(int arr[],int temp[],size_t left,size_t right)
{
	if(left < right)
	{
		int p = (left+right) / 2;
		_merge_sort(arr,temp,left,p);
		_merge_sort(arr,temp,p+1,right);
		merge(arr,temp,left,p,right);
	}
}

// 调用函数
void merge_sort(int arr[],size_t len)
{
	int temp[len];
	_merge_sort(arr,temp,0,len-1);
}

堆排序:把待排序的数据看成一个完全二叉树,要想实现堆排序,必须直到下列的表示:
arr[i] 表示根
arr[i * 2 + 1] 表示左
arr[i * 2 +2] 表示右
len / 2 是最后一个不是叶子结点的结点

void _heap_sort(int arr[],int r,int len)
{
	int l = r*2+1;
	if(l < len && arr[r] < arr[l])
	{
		swap(arr[r],arr[l]);
		_heap_sort(arr,l,len);
	}
	if(l+1 < len && arr[r] < arr[l+1])
	{
		swap(arr[r],arr[l+1]);
		_heap_sort(arr,l+1,len);
	}
}

void heap_sort(int arr[],int len)
{
	// 把每一个根结点都变量 根、左、右 中最大的
	for(int i=len/2; i>=0; i--)
		_heap_sort(arr,i,len);

	for(int i=len-1; i>0; i--)
	{
		swap(arr[0],arr[i]);
		_heap_sort(arr,0,i);
	}
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值