数据结构(排序)

插入排序

直接插入排序

元素集合越接近有序,直接插入排序的时间效率越高

时间复杂度:O(N^2)  最坏情况:逆序(每次挪动的数据都比上一个小)

                     O(N)      最好情况:顺序

空间复杂度:O(1)

性能稳定

 

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void InsertSort(int* a,int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
			a[end + 1] = tmp;//若数据最小,则此时end=-1,出循环
		}
	}
}

void SortPrint(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}
int main()
{
	int a[] = { 2,45,54,34,23,4,5,6,57 };
	int n = sizeof(a) / sizeof(int);
	InsertSort(&a, n);
	SortPrint(&a, n);
}

希尔排序( 缩小增量排序 )

先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

 

希尔排序:              时间复杂度   O(N^1.3)

1.预排序: 让数组更接近有序

2.插入排序

gap越大,大的可以越快跳到后面,小的数可以越快跳到前面

gap越小,跳的越慢,但是越接近有序,当gap==1相当于插入排序就有序了

 

 

多组并走

int gap = 3;
for (int i = 0; i < n - gap; i++)//范围防止越界访问
{
	int end = i;
	int tmp = a[end + gap];
	while (end >= 0)
	{
		if (tmp < a[end])
		{
			a[end + gap] = a[end];
			end -= gap;
		}
		else
		{
			break;
		}
	}
	a[end + gap] = tmp;
}

 一组一组地排序 (一组一组单独排,共排gap组)

int gap = 3;
for (int j = 0; j < gap; j++)
{
	for (int i = j; i < n - gap; i += gap)//范围防止越界访问
	{
		int end = i;
		int tmp = a[end + gap];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + gap] = a[end];
				end -= gap;
			}
			else
			{
				break;
			}
		}
		a[end + gap] = tmp;
	}
}
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void ShellSort()
{
	int a[] = { 10,200,10,65,25,65,84,25 };
	int  n = sizeof(a) / sizeof(int);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	int gap = n;
	while (gap > 1)
	{
        //+1保证最后一个gap一定是1
        //gap>1时是预排序
        //gap==1时是插入排序
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)//范围防止越界访问
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
int main()
{
	
	ShellSort();
}

选择排序 

 选择排序

选择排序 加强版:选出最大和最小的数排到左右两边 

时间复杂度: O(N^2)  (好,坏情况)

 

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

void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
//选择排序
void SelectInsert()
{
	int a[] = { 10,200,10,65,25,65,84,25 };
	int  n = sizeof(a) / sizeof(int);
	int begin = 0;
	int end = n - 1;

	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	while (begin < end)
	{
		int mn = begin;
		int mx = end;
		for (int i = begin + 1; i <= end; i++)
		{
			if (a[i] < a[mn])
			{
				mn = i;
			}
			if (a[i] > a[mx])
			{
				mx = i;
			}
		}
		swap(&a[begin], &a[mn]);
		if (begin == mx)
		{
			mx = mn;
		}
		swap(&a[end], &a[mx]);
		--end;
		++begin;

	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
int main()
{
	SelectInsert();
}

 交换排序

冒泡排序

1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2)    最坏情况

                         O(N)        最好情况(已经有序)
3. 空间复杂度:O(1)
4. 稳定性:稳定

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

void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)//n-1次排序,确定后n-1个位置
	{	
		int exchange = 0;
		for (int j = 0; j < n - 1 - i; j++)//进行一趟的交换,由于一次交换能确定一个最大位        
                                           //置,故每次的最大位置不同
		{
			if (a[j] > a[j + 1])
			{
				swap(&a[j], &a[j + 1]);
				exchange = 1;
			}
		}
		if (exchange == 0) break;
	}
}
void SortPrint(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}
int main()
{
	int a[] = { 2,45,54,34,23,4,5,6,57 };
	int n = sizeof(a) / sizeof(int);
	BubbleSort(&a, n);
	SortPrint(&a, n);
}

快速排序(重点)

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:    O(N*logN)

hoare版 

 

 

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

void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
//快排序
void QuickSort(int* a, int left, int right)
{
	// 区间只有一个值或者不存在就是最小子问题
	if (left >= right)
		return;

	int begin = left, end = right;

	int keyi = left;
	while (left < right)
	{
		// right先走,找小
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}

		// left再走,找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}

		swap(&a[left], &a[right]);
	}

	swap(&a[left], &a[keyi]);
	keyi = left;

	// [begin, keyi-1]keyi[keyi+1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}
int main()
{
	int a[] = { 2,3,495,56,7,85,5,43,3,21 };
	int left = 0;
	int right = sizeof(a) / sizeof(int) - 1;
	QuickSort(&a, left, right);
	for (int i = 0; i < sizeof(a)/sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

快排在有序情况下会有栈溢出的风险(递归深度太深)

避免在有序情况下,效率退化

1.随机选key

2.三数去中

在相同数较多的情况下,快排占优势

三数去中(解决栈溢出问题)

 

递归到小的子区间时,可以考虑使用插入排序(小区间的优化)
// 避免有序情况下,效率退化
// 1、随机选key
// 2、三数取中

int GetMidi(int* a, int left, int right)
{
	int midi = (left + right) / 2;
	// left midi right
	if (a[left] < a[midi])
	{
		if (a[midi] < a[right])
		{
			return midi;
		}
		else if (a[left] < a[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	else // a[left] > a[midi]
	{
		if (a[midi] > a[right])
		{
			return midi;
		}
		else if (a[left] < a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}


void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;

	// 小区间优化,不再递归分割排序,减少递归的次数
	if ((right - left + 1) < 10)
	{
		InsertSort(a+left, right - left + 1);
	}
	else
	{
		// 三数取中
		int midi = GetMidi(a, left, right);
		Swap(&a[left], &a[midi]);

		int keyi = left;
		int begin = left, end = right;
		while (begin < end)
		{
			// 右边找小
			while (begin < end && a[end] >= a[keyi])
			{
				--end;
			}

			// 左边找大
			while (begin < end && a[begin] <= a[keyi])
			{
				++begin;
			}

			Swap(&a[begin], &a[end]);
		}

		Swap(&a[keyi], &a[begin]);
		keyi = begin;
		// [left, keyi-1] keyi [keyi+1, right]
		QuickSort(a, left, keyi - 1);
		QuickSort(a, keyi + 1, right);
	}
}
L和R相遇点与key交换的原因(相遇点的值一定比key小[前提:key在最左边]) 

左边做key,右边先走,可以保证相遇位置比key小

相遇场景分析:

L遇R:R先走,停下来,R停下来的条件是遇到比key小的值,R停的位置比key小,L没有找到比key大的,遇到R就停下来了

R遇L:R先走,找小,没有找到比key小的,直接跟L相遇了,L停留的位置是上一轮交换的位置,上一轮交换,把比key小的值换到了L的位置

挖坑法(效率没有提升,但是思路更清晰,考虑点简单)

前后指针法

  

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值