数据结构常见的八个排序

目录

一 插入排序: 1,直接插入排序 2,希尔排序

 2希尔排序:

选择排序

堆排序

 交换排序 人名:冒泡排序  最简单的排序 但是稳定

快速排序三种递归方法的实现: 

快速排序的非递归方法的实现::

归并排序:

计数排序


一 插入排序: 1,直接插入排序 2,希尔排序

1直接插入排序:基本思想 把待排序的记录按照器关键码值的大小逐个插到一个已经排好序的有序序列中,知道所有的记录插入完为止,得到一个新的有序序列。如插扑克牌

直接插入排序的特性总结: 1. 元素集合越接近有序,直接插入排序算法的时间效率越高 2. 时间复杂度:O(N^2) 3. 空间复杂度:O(1),它是一种稳定的排序算法 4. 稳定性:稳定

代码演示

void insertsort(int *a,int n)
{
 for(int i=0;i<n-1;i++)   //注意这里是n-1 因为最后end= n-2 x为n-1;
{
   int end =i;
   int x=a[end+1];
 while(end>=0)
{
  if(a[end]>x)
{
   a[end+1]=a[end];
   end=end-1;
}
else
{
break;
}
}
a[end+1]=x;   
}
}

 2希尔排序:

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

简单可以理解为就是先分成几个组排  然后在合并成最有一个组排 代码实现的时候相比较之间的直接插入排序就是给一个间距值就可以。

希尔排序的特性总结: 1. 希尔排序是对直接插入排序的优化。

2. 当gap > 1时都是预排序,目的是让数组更接近于有序。

3 当gap == 1时,数组已经接近有序的了,这样就 会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

4 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的 希尔排序的时间复杂度都不固定. 约为O(1.3)  稳定性: 不稳定

代码实现 

void shellsort(int *a,int k,int n)
{
   int gap=k;  //初始给一个gap值
//第一种写法
for(int i=0;i<gap;i++)
{
for(int j=i;j<n-gap;j+=gap)
{
    int end =j;
    int x=a[end+gap];
   while(end>=0)
{
   if(a[end]>x)
{
 a[end]=a[end+gap];
end-=gap;
}
else
{
break;
}
}
a[end+gap]=x;
}
}
}

希尔排序 代码优化 一锅炖了

//一锅炖

void shellsort(int *a,int gapp,int n)
{
   int gap=gapp;
 while(gap>1)
{

   gap=gap/3 + 1;

for(int j=0;j < n-gap ; j++)
{
   if(a[end]>x)
{
  a[end+gap]=a[end];
   end-=gap;
}
else
{
break;
} 
a[end+gap]=x;
}
}

选择排序

选个排序感觉贼tm垃圾

基本思想::每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的 数据元素排完 。   说人话就是 将begin元素一开始设置为最大最小值,然后遍历一遍数组 每次得到这次遍历结果的最大最小值 本次遍历结果的最小值就放在begin处 最大值就放在end处然后begin++ end--  

 代码:

void swap(int *a,int*b)
{
int k=*a;
*a=*b;
*b=k;
}
void selectsort(int *a,int n)
{

int begin =0;
int end=n-1;
 while(begin<end)
{
int minx=begin;
int maxx=begin;
for(int i=0;i<n;i++)
{
  if(a[i]>a[maxx])
{
maxx=i;
}
if(a[i]<a[minx])
{
  minx=i;
}

}
swap(&a[begin],&a[minx]);
//注意这个时候 可能第一就是最大的 已经被你换走了 这个时候要记得maxx和编号换一下;
if(maxx==begin)
{
maxx=minx;
}
swap(&a[maxx],&a[end]);
begin++;
end--;

}
}

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用 2. 时间复杂度:O(N^2) 3. 空间复杂度:O(1) 4. 稳定性:不稳定

堆排序

堆排序还是可以的 但是如果对c来说 你还要建立堆  当然如果是对c++来说就很好了。这里我就从新建立一个堆 然后实现一个堆排序:堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是 通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。解释一下为啥你如果是升序你要建立一个大堆  大堆由大到小 如果你建立一个有小到大的小堆,那么如果第一个数就是最小的 那完犊子了 只有的数在也进不去了。。

//建立一个堆
给出堆建立的一个关键代码 分别是 adjustdown;

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 选出左右孩子中小的那一个
		if (child + 1 < n && a[child + 1] > a[child]) //建立大堆这里 child哪个大就选哪里 如果是小堆哪个小就选哪个。
		{
			++child;
		}

		// 如果小的孩子小于父亲,则交换,并继续向下调整
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

// 堆排序 -- O(N*logN)
void HeapSort(int* a, int n)
{
	// O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	// O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}

 交换排序 人名:冒泡排序  最简单的排序 但是稳定

根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排 序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。冒泡排序的特性总结: 1. 冒泡排序是一种非常容易理解的排序 2. 时间复杂度:O(N^2) 3. 空间复杂度:O(1) 4. 稳定性:稳定 冒泡排序之间给代码了 没人不会写这个的

void bubblesort(int *a,int n)
{
for(int i=0;i<n;i++)
{

for(intj=0;j<n-1-i;j++)
{

if(a[j]>a[j+1])
{

swap(&a[j],&a[j+1]); //前面选择写了swap 这里就不写了;
}

}
}
}

快速排序三种递归方法的实现: 

快速排序非常重要,有递归跟非递归的区别,有前后指针 两头指针 挖坑法 和三维取中的一些技巧。

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。说人话 有一组数据  hoare如果它取最左边的当基准值 那么就走右序列开始走 走找到比这个基准值小的数 然后左序列再走 走找到比这个基准值大的值 。完事后交换,最后走到重逢处将基准值与这个位置的值交换一下。完事多次递归 

先给hoare方法的快速排序::

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

else   //a[left]<=a[mid]
{
    if(a[left]>a[right])
{
return left;
}
if(a[mid]<a[right])
{
return mid;
}
else
{
return right;
}

}
}
int quicksort (int *a,int left,int right)
{
 int p=getmidvalue(a,left,right);
swap(&a[left],&a[p]);
int key=left;
while ( left < right )
{
//记住左边当key应该右边先走
  while(left<right && a[right]>a[key])
{
   right--;
}
while(left<right&&a[left]<a[key])
{
left++;
}
//走完循环后,这个时候两个指针都走到了对应的位置 然后换
 swap(&a[left],&a[right]);
}
return left;
}

void Quicksort(int *a,int left,int right)
{

  if (left>=right)
{
return;
}
int par=quicksort(a,left,right);
  Quicksort(a,left,par);
  Quicksort(a,par+1,right);

}

挖坑法  挖坑的目的就是先保存基准值 然后把基准值的地方变成一个坑,如果左边为基准值 那么我们右边 先走, 找到一个比左边小的值 将其放入左边的坑中,这样右边成为一个坑了 接着左边走  ,

代码实现:

//挖坑法实现快速排序 这也是递归的方法 会给3个递归的方法 然后一个非递归的方法
int  quicksort2(int *a, int left,int right)
{
   int k=getmidvalue(a,left,right)//前面已给这个函数 这里不写了。
   swap(&a[left],&a[k]);
    int key=left;
   int key1=a[left];
   int piovit=a[left];
while(left<right)
{
 while(left<right && a[right]>a[key])
{
right--;
}
swap(&a[key],&a[right]);
key=right;
while(left<right&&a[left]<a[key])
{
left++;
}
swap(&a[key],&a[left]);
key=left;
}
a[key]=key1;
return key;
}

 前后指针方法::

就是给一个prev和cur  两个指针 只不过现在不是从两端开始走了 而是这是cur先走 如果走到比a[key]值小的地方 那么就放a[++prev]=a[cur];

//前后指针方法 也推荐使用这种方法  快速排序最简单的way:

void quicksort(int *a,int left,int right)
{
 int k=getmidvalue(a,left,right)//前面已给这个函数 这里不写了。
   swap(&a[left],&a[k]);
    int key=left;
  int prev=left;
  int cur=prev+1;
  while(cur<=right)
{
     while(cur<=right && a[cur]>=a[key])
      {
      cur++;
      }

     if(a[cur]<=right)
{


  swap(&a[++prev],&a[cur]);
   cur++;
}
   }
swap(&a[prev],&a[key]);
return prev;
}
}

快速排序的非递归方法的实现::

       非递归方法就是要用到栈的概念。 具体过程可以理解 建立一个栈 然后当每次递归的区间放进去  注意 栈是先进后出的 比如说你先放begin 后放end 那么出来的时候就是先end 后begin  然后如果是想先往左递归,那么你需要先放入右区间再放入左区间(栈的代码这里就不给了 之前的博客给过的  需要的朋友之间取看一下谢谢)

// 递归深度太深的程序,只能考虑改非递归
void QuickSortNonR(int* a, int left, int right)
{
	ST st;
	StackInit(&st);
	StackPush(&st, left);
	StackPush(&st, right);

	while (!StackEmpty(&st))
	{
		int end = StackTop(&st);
		StackPop(&st);
		int begin = StackTop(&st);
		StackPop(&st);
		int keyi = Partion3(a, begin, end);
		// [begin, keyi-1] keyi [keyi+1, end]
		if (keyi + 1 < end)
		{
			StackPush(&st, keyi+1);
			StackPush(&st, end);
		}
		if (begin < keyi-1)
		{
			StackPush(&st, begin);
			StackPush(&st, keyi-1);
		}
	}
	StackDestroy(&st);
}

归并排序:

 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有 序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

简单理解为就是先分解 一步一步的分解 (需要递归) 分解成单体 然后在一一合并 之间链表那里有用过归并  

直接给代码

void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left >= right)
	{
		return;
	}

	int mid = (left + right) / 2;
	// [left, mid] [mid+1, right] 有序
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid + 1, right, tmp);

	int begin1 = left, end1 = mid;
	int begin2 = mid+1, end2 = right;
	int i = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}

	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}

	// tmp 数组拷贝回a
	for (int j = left; j <= right; ++j)
	{
		a[j] = tmp[j];
	}
}




void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	_MergeSort(a, 0, n - 1, tmp);

	free(tmp);
	tmp = NULL;
}


}

归并排序的非递归代码 ,,

                       

// 时间复杂度:O(N*logN)
// 空间复杂度:O(N)
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			// [i,i+gap-1] [i+gap,i+2*gap-1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			// 核心思想:end1、begin2、end2都有可能越界
			// end1越界 或者 begin2 越界都不需要归并
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			
			// end2 越界,需要归并,修正end2
			if (end2 >= n)
			{
				end2 = n- 1;
			}

			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}

			// 把归并小区间拷贝回原数组
			for (int j = i; j <= end2; ++j)
			{
				a[j] = tmp[j];
			}
		}

		gap *= 2;
	}

	free(tmp);
	tmp = NULL;
}

计数排序

 最后一个是计数排序优点是时间复杂度为O(N),就是创建一个数组  遍历一遍原数组 比如原数组是0到5的范围 那么没遇到0就在计数数组0处加1

void CountSort(int* a, int n)
{
	int max = a[0], min = a[0];
	for (int i = 1; i < n; ++i)
	{
		if (a[i] > max)
		{
			max = a[i];
		}

		if (a[i] < min)
		{
			min = a[i];
		}
	}

	int range = max - min + 1;
	int* count = (int*)malloc(sizeof(int)*range);
	memset(count, 0, sizeof(int)*range);
	if (count == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	// 统计次数
	for (int i = 0; i < n; ++i)
	{
		count[a[i] - min]++;
	}

	// 根据次数,进行排序
	int j = 0;
	for (int i = 0; i < range; ++i)
	{
		while (count[i]--)
		{
			a[j++] = i + min;
		}
	}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值