排序算法总结

本文详细介绍了排序算法的原理和实现,包括直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序和计数排序等。对每种排序算法的稳定性、时间复杂度和空间复杂度进行了分析,适合学习排序算法的读者参考。
摘要由CSDN通过智能技术生成

排序算法总结

【目录】

  • 1.排序的概念及其运用
  • 2.常见排序算法的实现
  • 3.排序算法复杂度及稳定性分析

1.排序的概念及其运用
1.1排序的概念
排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。
1.2排序运用
在这里插入图片描述
在这里插入图片描述
1.3 常见的排序算法
在这里插入图片描述
2.常见排序算法的实现
2.1 插入排序
2.1.1基本思想:
直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
2.1.2直接插入排序:
当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。
2.1.2.1直接插入排序理解:
在这里插入图片描述
2.1.2.2直接插入排序代码实现:

#include<stdio.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(a[end]>tmp)
			 {
			  a[end+1]=a[end];
			  --end;
			 } 
			else 
			  break;
		}
		a[end+1]=tmp;	
	}
}
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
void Test()
{
	int a[]={2,5,1,8,6,9,3,4,7};
	InsertSort(a,9);
	print(a,9);	
} 
int main()
{
    Test();
	return 0;	
}

直接插入排序的特性总结:

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

2.1.3 希尔排序( 缩小增量排序 ):
希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。
2.1.3.1希尔排序理解:
在这里插入图片描述
在这里插入图片描述
2.1.3.2希尔排序代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void ShellSort(int *a,int n) //希尔排序 
{
	int gap=n;
	while(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;	
       }	
	}
}
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
void Test2()
{
    int a[]={2,5,1,8,6,9,3,4,7};
	ShellSort(a,9);
	printf("希尔排序结果为:\n");
	print(a,9);		
}
int main()
{
	Test2();
	return 0;
} 

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就 会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N1.3—N2)
  4. 稳定性:不稳定

2.2 选择排序:
2.2.1基本思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
2.2.2 直接选择排序:

  • 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素。
  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换。
  • 在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素。

2.2.2.1直接选择排序理解:
在这里插入图片描述2.2.2.2 直接选择排序代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
void SelectSort(int *a,int n)  //选择排序 
{
	int begin=0;
	int end=n-1;
	int min,max;
	while(begin<=end)
	{
	   min=max=begin;
	   for(int i=begin+1;i<=end;++i)
	   {
	     if(a[i]<a[min])
		 {
		 	min=i;
		 }
		 if(a[i]>a[max])
		 {
		 	max=i;
		 }	
	   }	
	   swap(&a[begin],&a[min]);
	   if(max==begin)
	   {max=min;}
	   swap(&a[end],&a[max]);
	   ++begin;
	   --end;
	   print(a,n);
	}
}
void Test3()
{
    //int a[]={2,5,1,8,6,9,3,4,7};
    int a[]={7,4,5,9,8,2,1};
    SelectSort(a,7);
    printf("选择排序结果为:\n");
	print(a,7);		
}
int main()
{
	Test3();
	return 0;
} 

2.2.2.3 直接选择排序理解验证:
在这里插入图片描述

直接选择排序的特性总结:

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

2.2.3 堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
2.2.3.1 堆排序理解:
在这里插入图片描述
在这里插入图片描述
2.2.3.2 堆排序代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
void AdjustDown(int *a,int n,int parent)//堆的向下调整建大堆 
{
	int child = parent*2+1;
	while(child<n)
	{
		if(child+1<n&&a[child]<a[child+1])
		 ++child;
		if(a[child]>a[parent])
		{
			swap(&a[child],&a[parent]);
			parent=child;
			child=parent*2+1;
		}
		else
		 break;
	}
}
void HeapSort(int *a,int n)//堆排序 
{
	//建大堆
	for(int i=(n-2)/2;i>=0;--i)
	{
		AdjustDown(a,n,i);
	 }
	//选数排序
	int end=n-1;
	while(end>0)
	{
		swap(&a[0],&a[end]);
	    AdjustDown(a,end,0);
	    --end;
	 } 
}
void Test4()
{
    int a[]={20,17,4,16,5,3};
    HeapSort(a,6);
    printf("堆排序结果为:\n");
	print(a,6);		
}
int main()
{
	Test4();
	return 0;
} 

堆排序的特性总结:

  1. 堆排序使用堆来选数,效率就高了很多。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定

2.3 交换排序
2.3.1 基本思想
所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
2.3.2冒泡排序
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
2.3.2.1冒泡排序理解:
在这里插入图片描述
2.3.2.2冒泡排序代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
void BubbleSort(int *a,int n)//冒泡排序 
{
	int end=n;
	while(end>0)
	{
	  for(int i=1;i<end;++i)
	  {
	  	if(a[i-1]>a[i])
	  	{
	  	  swap(&a[i-1],&a[i]);	
		}
	  }	
	  --end;
	  print(a,n);
	}
}
void Test5()
{
	int a[]={25,6,56,24,9,12,55};
	BubbleSort(a,7);
	printf("冒泡排序结果为:\n");
	print(a,7);	
}
int main()
{
	Test5();
	return 0;
} 

2.3.2.3冒泡排序理解验证:
在这里插入图片描述

冒泡排序的特性总结:

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

2.3.3 快速排序
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

将区间按照基准值划分为左右两半部分的常见方式有:

  1. hoare版本(左右指针版本)
  2. 挖坑法
  3. 前后指针版本

2.3.3.1(1) 快速排序左右指针法理解:
在这里插入图片描述
在这里插入图片描述
2.3.3.2(1) 快速排序左右指针法代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
int PartSort1(int *a,int left,int right)//左右指针法 
{
	int key=a[right];
	int keyindex=right;
	while(left<right)
	{
		//left找大,right找小,再交换(右边为k,左边先走;左边为k,右边先走)
		while(left<right&&a[left]<=key)
		  ++left;
		while(left<right&&a[right]>=key)
		  --right;

		swap(&a[left],&a[right]);
	}
	swap(&a[left],&a[keyindex]);
	return left;
}
void QuickSort1(int *a,int left,int right)//左右指针法
{
   if(left>=right)
    return ;
   int keyindex=PartSort1(a,left,right);
   QuickSort1(a,left,keyindex-1);
   QuickSort1(a,keyindex+1,right);
   	
}
void Test8()
{
	int a[]={6,1,2,7,9,3,4,5,10,8};
	QuickSort1(a,0,9);
	printf("快速排序左右指针法结果为:\n");
	print(a,10);
}
int main()
{
	Test7();
	Test8();
	return 0;
}

2.3.3.1(2) 快速排序挖坑法基本思想:

  1. 定义两个指针left指向起始位置,right指向最后一个元素的位置,然后指定一个基数key(right),作为坑。
  2. left寻找比基数(key)大的数字,找到后将left的数据赋给right,left成为一个坑,然后right寻找比基数(key)小的数字,找到将right的数据赋给left,right成为一个新坑,循环这个过程,直到begin指针与end指针相遇,然后将key返回给那个坑(最终:key的左边都是比key小的数,key的右边都是比key大的数),然后进行递归操作。

2.3.3.2(2) 快速排序挖坑法理解:
在这里插入图片描述
2.3.3.3(2) 快速排序挖坑法代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
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;	
   }	

} 
int GetmidIndex(int *a,int left,int right)
{
	int mid=left+(right-left)/2;
	if(a[left]<a[mid])
	{
		if(a[mid]<a[right])
		 return mid;
		else if(a[left]>a[right])
		       return left;
		     else
		       return right;
	}
	else//a[left]>a[mid]
	{
		if(a[mid]>a[right])
		 return mid;
		 else if(a[left]<a[right])
		      return left;
		      else
		      return right;
	}
}
int PartSort2(int *a,int left,int right)//挖坑法
{  
   int mid=GetmidIndex(a,left,right);//快速排序优化:三数取中
   swap(&a[mid],&a[right]);
   int key=a[right];
   int keyindex=right;
   while(left<right)
	{
		while(left<right&&a[left]<=key)	//left找大,放到右边的坑
		{
			++left;//left找大 
		}
		a[right]=a[left];//赋值,left作为新坑 
		while(left<right&&a[right]>=key)//right找小,放到左边的坑
		{
			--right;//right找小 
		} 
		a[left]=a[right];//赋值,right作为新坑 
    }
	a[left]=key;
	return left;	
}
void QuickSort2(int *a,int left,int right)//挖坑法
{
   if(left>=right)
    return ;
   if(right-left+1>10)
   {
      int keyindex=PartSort2(a,left,right);
	  QuickSort2(a,left,keyindex-1);
	  QuickSort2(a,keyindex+1,right);
   }	
   else
      InsertSort(a+left,right-left+1);//快速排序优化:小区间有序则用直接插入实现 
}
void Test8()
{
	int a[]={6,1,2,7,9,3,4,5,10,8};
	QuickSort2(a,0,9);
	printf("快速排序挖坑法结果为:\n");
	print(a,10);
}
int main()
{
	Test8();
	return 0;
}

2.3.3.1(3) 快速排序前后指针法基本思想:

定义两个指针,一前一后,cur(前)指向起始位置,prev(后)指向cur的前一个位置;选择数组最后一个元素作为key(right)。
实现过程:cur找比key小的数,prev在cur没有找到的情况下,一直不动(即保证prev一直指向比key大的数);直到cur找到比key小的数(+ +prev && prev != cur时)然后++cur,prev仍然不动。
直到cur走到right前一个位置,终止循环。最后++prev,交换prev和right的值。返回中间位置prev。最后再继续递归。

2.3.3.2(3) 快速排序前后指针法理解:
在这里插入图片描述
2.3.3.3(3) 快速排序前后指针法代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
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;	
   }	

} 
int PartSort3(int *a,int left,int right)//前后指针法 
{
   int 	key=a[right];
   int prev=left-1;
   int cur=left;
   while(cur<right)
   {
   	 if(a[cur]<key&&++prev!=cur)
   	  swap(&a[cur],&a[prev]);
   	  ++cur;
   }
   ++prev;
   swap(&a[right],&a[prev]);
   return prev;
} 
void QuickSort3(int *a,int left,int right)//前后指针法
{
   if(left>=right)
    return ;
   if(right-left+1>10)
   {
      int keyindex=PartSort3(a,left,right);
	  QuickSort3(a,left,keyindex-1);
	  QuickSort3(a,keyindex+1,right);
   }	
   else
   InsertSort(a+left,right-left+1);//快速排序优化:小区间有序:用直接插入实现 
}
void Test9()
{
	int a[]={6,1,2,7,9,3,4,5,10,8};
	QuickSort3(a,0,9);
	printf("快速排序前后指针法结果为:\n");
	print(a,10);
}
int main()
{
	Test9();
	return 0;
}

2.3.3.1(4) 快速排序非递归法基本思想:

  1. 左右区间入栈(先右后左)
  2. 取栈顶元素,出栈
  3. 排序
  4. 入栈,先右后左(直到栈为空,停止循环)

2.3.3.2(4) 快速排序非递归法代码实现:

#include<stdio.h>
#include <assert.h>
#include <malloc.h>
#include <stdbool.h>

typedef int STDataType;
struct Stack
{
	STDataType* _array;
	size_t _top;
	size_t _capacity;
};

typedef struct Stack Stack;

void StackInit(Stack* ps);
void StackDestory(Stack* ps);
void StackPush(Stack* ps, STDataType x);
void StackPop(Stack* ps);
STDataType StackTop(Stack* ps);
size_t StackSize(Stack* ps);
bool StackEmpty(Stack* ps);

void StackInit(Stack* ps)
{
	assert(ps);
	ps->_array = NULL;
	ps->_top = 0;
	ps->_capacity = 0;
}

void StackDestory(Stack* ps)
{
	assert(ps);
	if (ps->_array != NULL)
	{
		free(ps->_array);
		ps->_array = NULL;
		ps->_capacity = ps->_top = 0;
	}
}

void StackPush(Stack* ps, STDataType x)
{
	assert(ps);
	if (ps->_top == ps->_capacity)
	{
		size_t newcapacity = ps->_capacity == 0 ? 2 :ps->_capacity * 2;
		ps->_array = (STDataType*)realloc(ps->_array, newcapacity*sizeof(STDataType));
		ps->_capacity = newcapacity;
	}

	ps->_array[ps->_top] = x;
	ps->_top++;
}

void StackPop(Stack* ps)
{
	assert(ps && ps->_top > 0);
	--ps->_top;
}

STDataType StackTop(Stack* ps)
{
	assert(ps && ps->_top > 0);

	return ps->_array[ps->_top - 1];
}

size_t StackSize(Stack* ps)
{
	assert(ps);

	return ps->_top;
}

bool StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->_top == 0;
}
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
int PartSort3(int *a,int left,int right)//前后指针法 
{
   int 	key=a[right];
   int prev=left-1;
   int cur=left;
   while(cur<right)
   {
   	 if(a[cur]<key&&++prev!=cur)
   	  swap(&a[cur],&a[prev]);
   	  ++cur;
   }
   ++prev;
   swap(&a[right],&a[prev]);
   return prev;
} 
void QuickSort(int *a,int left,int right)//用栈实现快速排序非递归 
{
	Stack s;
	StackInit(&s);
	StackPush(&s,left);
	StackPush(&s,right);
	while(!StackEmpty(&s))
	{
		int end=StackTop(&s);
		StackPop(&s);
		int begin=StackTop(&s);
		StackPop(&s);
		int keyindex=PartSort3(a,begin,end);
		if(begin<keyindex-1)
		{
			StackPush(&s,begin);
			StackPush(&s,keyindex-1);
		}
		if(keyindex+1<end)
		{
			StackPush(&s,keyindex+1);
			StackPush(&s,end);
		}
		
	}
}
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
void Test()
{
	int a[]={2,5,1,8,6,9,3,4,7};
	QuickSort(a,0,8);
    printf("快速排序非递归结果为:\n"); 
	print(a,9);	
} 
int main()
{
    Test();
	return 0;	
}

2.3.3.4 快速排序优化

  1. 三数取中法选key
  2. 递归到小的子区间时,可以考虑使用插入排序
    已经在快速排序挖坑法中标明。

快速排序的特性总结:

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

2.4 归并排序
2.4.1 归并排序基本思想:
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
2.4.2 归并排序理解:
在这里插入图片描述
2.4.2.1 归并排序代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
void _MergeSort(int *a,int left,int right,int *tmp)//归并排序 
{
	if(left==right)
	 return ;
	int mid=left+(right-left)/2;
	//[left,mid],[mid+1,right] 先拆分
	_MergeSort(a,left,mid,tmp); 
	_MergeSort(a,mid+1,right,tmp);
	//取数 归下来 排序 拷回去 [left,mid],[mid+1,right]归并到tmp[left,right]
	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];
			++begin1;
		}
		else
		{
			tmp[i++]=a[begin2];
			++begin2;
		}
	 } 
	 while(begin1<=end1)
	 {
	 	tmp[i++]=a[begin1];
		++begin1;
	 }
	 while(begin2<=end2)
	 {
	 	tmp[i++]=a[begin2];
		++begin2;
	 }
	 memcpy(a+left,tmp+left,sizeof(int)*(i-left));
}
void MergeSort(int *a,int n) 
{
	int *tmp=(int*)malloc(sizeof(int)*n);
	_MergeSort(a,0,n-1,tmp);
	free(tmp);
}
void Test6()
{
	int a[]={2,5,1,8,6,9,3,4,7};
	MergeSort(a,9);
	printf("归并排序结果为:\n");
	print(a,9);	
}
int main()
{
	Test6();
	return 0;
}

2.4.2.2 归并排序非递归代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)* n);
	
	// [begin1,end1][begin2,end2]
	int gap = 1;
	while (gap < n)
	{
		for (int begin = 0; begin < n; begin += 2*gap)
		{
			// [begin, begin+gap-1][begin+gap, begin+2*gap-1]
			// [0, 0][1, 1]  gap = 1
			// [0, 1][2, 3]	 gap = 2

			int begin1 = begin, end1 = begin+gap-1;
			if (end1 >= n)
				end1 = n - 1;

			int begin2 = begin+gap, end2 = begin+2*gap-1;
			if (end2 >= n)
				end2 = n - 1;

			int index = begin1;
			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++];

		}
		memcpy(a, tmp, sizeof(int)*n);

		gap *= 2;
		print(a, n);
	}
	free(tmp);
}
void Test6()
{
	int a[]={2,5,1,8,6,9,3,4,7};
	MergeSortNonR(a,9);
	printf("归并排序结果为:\n");
	print(a,9);	
}
int main()
{
	Test6();
	return 0;
}

归并排序的特性总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

2.5 计数排序
2.5.1 计数排序基本思想:
计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤:

  1. 统计相同元素出现次数。
  2. 根据统计的结果将序列回收到原来的序列中。

2.5.2 计数排序理解:
在这里插入图片描述
2.5.2.1 计数排序代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void swap(int* m,int *n)
{
	int tmp=*m;
	*m=*n;
	*n=tmp; 
} 
void print(int *a,int n)
{
   for(int i=0;i<n;++i )
   {
   	printf("%d ",a[i]);
   }
   printf("\n");
}

void Test6()
{
	int a[]={2,5,1,8,6,9,3,4,7};
	CountSort(a,9);
	printf("计数排序结果为:\n");
	print(a,9);	
}
int main()
{
	Test6();
	return 0;
}

计数排序的特性总结:

  1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
  2. 时间复杂度:O(MAX(N,范围))
  3. 空间复杂度:O(范围)
  4. 稳定性:稳定

3.排序算法复杂度及稳定性分析
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值