一、排序的基本概念
1、排序是按关键字的非递减或非递增顺序对一组记录重新进行排列的操作。
2、排序的稳定性
3.内部排序和外部排序
内部排序:1.插入类:直接插入排序、折半插入排序、希尔排序
2.交换类:冒泡排序、快速排序
3.选择类:简单选择排序、树形选择排序、堆排序
4.归并类:2-路归并排序
5.分配类:基数排序
二、插入类排序
1.直接插入排序
#include<stdio.h>
void Print(int array[],int len){
for(int i=0;i<len;i++){
printf("%d ",array[i]);
}
printf("\n");
}
/*直接插入排序*/
/*
*算法描述:
*1.将待排序序列分为两部分,一部分有序一部分无序
*2.第一个元素为有序序列,从第二个元素到最后为无序序列
*3.将无序序列中每一个元素依次插入到有序序列的合适位置--从小到大(从大到小)
*合适的位置:待排序元素大于或等于(小于)该元素
*/
void InsertSort(int array[],int len){
int i,j;
//第一个for循环 遍历无序序列
for(i=1;i<len;i++){ //从数组的第二个元素开始依次遍历无序序列
int tem = array[i]; //临时保存将要排序的元素
//第二个for循环遍历有序序列
for(j=i-1;tem<=array[j]&&j>=0;j--){ //将待排序元素依次和有序序列中的元素比较
//待排序元素 小于 有序序列中当前元素时 将该元素后移
array[j+1] = array[j];
}
array[j+1] = tem; //待排序元素 大于 有序序列最后一个元素 直接将该元素插入到有序序列最后
}
printf("\n排序完成!\n\n");
}
main(){
int array[10] = {4,3,10,5,6,7,1,2,8,9} ;
int len = sizeof(array) / sizeof(int);
printf("初始序列:\n");
Print(array,len);
InsertSort(array,len);
printf("排序后序列:\n");
Print(array,len);
}
时间复杂度:O(n2);
空间复杂度:O(1);
2.折半插入排序
typedef int ElementType;
void BinaryInsertSort(ElementType array[], int size)
{
int high;//高地址位置
int low;//低地址位置
int mid;//中端位置
int i;//指示无序组中的元素位置
int j;//指示有序组中的元素位置
ElementType temp;//临时变量,用于拷贝数据
for (i = 1; i < size; i++)//第一个位置默认有序
{
temp = array[i];//拷贝数据
high = i - 1;//指向有序组中最后一个有效数据
low = 0;//指向有序组中第一个数据位置
while (low<=high)//采用二分查找,在有序组中找待插入元素的位置
{
mid = (low + high) / 2;//中间位置为low于high和的一半
if (array[mid] < temp)//说明插入位置在mid的右边
{
low = mid + 1;//指向比较位置的后一个位置
}
else//说明插入位置在mid的左边
{
high = mid - 1;//指向比较位置的前一个位置
}
}
for (j = i; j >=low; j--)//将查找的位置元素及其后面的元素整体移动一个位置
{
array[j] = array[j - 1];
}
array[low] = temp;//插入到正确位置
}
}
时间复杂度:O(n2);
空间复杂度:O(1);
3.希尔排序
#include<stdio.h>
void Print(int array[],int len){
for(int i=0;i<len;i++){
printf("%d ",array[i]);
}
printf("\n");
}
/**希尔排序
*算法思想
* 1.希尔排序是针对直接插入排序的优化
* 2.对直接插入排序算法分析:
* 序列基本有序时移动次数少 排序效率高
* 序列长度短时排序移动次数和比较次数都少 排序效率高
* 3. 希尔排序是首先将待排序序列分割成多个长度短的子序列然后使用直接插入排序
* 但并不是直接将待排序序列平均分成若干个子序列,而是相隔某个增量值(δk)为一个子序列
* 4. 然后整个序列就基本有序了,在使用直接插入排序 (增量为1)
*/
void ShellSort(int array[],int len) {
int i,j;
int dk = len/2; //增量 k (增量任意取值,我这里每次取序列长度的一半)
while(dk>=1) { //增量为1即直接插入排序
/*直接插入排序变体开始*/
//(将代码中的dk替换成1则和直接插入排序是一样的)
for(i=dk;i<len;i++){ //易错此处是i++ 并不是 i+=dk
int tem = array[i];
for(j=i-dk;j>=0&&tem<array[j];j-=dk){
array[j+dk] = array[j];
}
array[j+dk] = tem;
}
/*直接插入排序变体结束*/
dk/=2;
}
}
int main(){
int array[]={4,10,34,54,12,3,1,55,15,13};
// 44 4 26
//31 3
//38 10
int len = sizeof(array) / sizeof(int);
printf("初始序列:\n");
Print(array,len);
ShellSort(array,len);
printf("排序后序列:\n");
Print(array,len);
}
时间复杂度:O(n3/2);
空间复杂度:O(1);
三.交换排序
1.冒泡排序
#include<stdio.h>
void Print(int array[],int len){
for(int i=0;i<len;i++){
printf("%d ",array[i]);
}
}
void BubbleSort(int array[],int len){
int tem;
//外层循环控制 排序的趟数 n个元素排序需要循环n-1次 【1】
for(int i=0;i<len-1;i++) {
//内层循环控制比较的次数 n个元素第i趟比较n-i次 【2】
for(int j=0;j<len-1-i;j++) {
//比较相邻的元素大小 目的:将最大的元素选出到移动到最后
if(array[j]>array[j+1]){
tem = array[j];
array[j] = array[j+1];
array[j+1] = tem;
}
}
}
printf("\n\n排序完成!\n");
}
int main(){
int array[] = {3,44,38,5,47,15,36};
int len = sizeof(array) / sizeof(int);
printf("原始序列为:\n");
Print(array,len);
BubbleSort(array,len);
printf("\n排序后序列为:\n");
Print(array,len);
}
时间复杂度:O(n2);
空间复杂度:O(1);
2.快速排序
#include<stdio.h>
void Print(int array[],int len){
for(int i=0;i<len;i++){
printf("%d ",array[i]);
}
printf("\n");
}
/*获取基准坐标,并相对有序(左边比基准坐标小,右边比基准坐标大)*/
int getStandard(int array[],int low,int high) {
int key = array[low]; //临时保存基准元素
while(low<high) {
//high指针从后向前遍历 , 元素比基准元素大则指针向前移动 则比基准元素小则和基准元素交换
while(low<high && array[high]>=key){
high--;
}
if(low<high){
array[low] = array[high]; //赋值给第一个元素,因为第一个元素作为基准元素已经临时保存了,所可以直接赋值
}
//low指针从前向后遍历 , 元素比基准元素小则指针向后移动 否则比基准元素大则和基准元素交换
while(low<high && array[low]<=key){
low++;
}
if(low<high){
array[high] = array[low]; //复制给high指针所指得位置,因为在11行已经赋值给array[low]了
}
}
array[low] = key;
return low;
}
void QuickSort(int array[],int low,int high){
if(low<high){ //递归出口
int standard = getStandard(array,low,high);
QuickSort(array,low,standard-1); //比基准元素小的部分继续调用快速排序
QuickSort(array,standard+1,high); //比基准元素大的部分继续调用快速排序
}
}
int main(){
int array[] = {3, 44, 38, 5, 47, 15, 36};
int size = sizeof(array) / sizeof(int);
printf("原始序列为:\n");
Print(array,size);
QuickSort(array,0,size-1);
printf("排序后序列为:\n");
Print(array,size);
}
时间复杂度:O(nlog2n);
空间复杂度:O(log2n)或O(n);
四.选择排序
简单选择排序
//交换两个数据
void Swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//选择排序
void SelectSort(int* arr, int size)
{
int begin = 0;
int end = size - 1;
while (begin < end)
{
int max = begin;
int min = begin;
int i = 0;
for (i = begin+1; i <= end; i++)
{
if (arr[i] < arr[min])
{
min = i;
}
if (arr[i] > arr[max])
{
max = i;
}
}
Swap(&arr[begin], &arr[min]);
if (begin == max) //修正max
{
max = min;
}
Swap(&arr[end], &arr[max]);
begin++;
end--;
}
}
时间复杂度:O(n^2)
空间复杂度:O ( 1 )
堆排序
private static void heapSort(int[] arr) {
int temp = 0;
//堆排序
for(int i = arr.length/2-1;i>=0;i--){
adjustHeap(arr,i,arr.length);
}
for (int j=arr.length-1;j>0;j--){
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr,0,j);
}
System.out.println("数组="+Arrays.toString(arr));
}
/**
* 将以i对应的非叶子节点的树调整成一个大顶堆
* 举例 int[] arr = {4,6,8,5,9};=>i=1 =>{4,9,8,5,6} => i=0 =>{9,6,8,5,4}
* @param arr
* @param i 表示非叶子节点在数组中的索引
* @param length 对多少个元素进行调整
*/
public static void adjustHeap(int[] arr,int i,int length){
//a[i]>a[2i+1]&&a[i]>a[2i+2]
int temp = arr[i];
for (int k=i*2+1;k<length;k=k*2+1){
//先比较左子节点和右子节点的大小,最大的那个和temp进行交换
if(k+1<length && arr[k]<arr[k+1]){
k++;//k指向右子节点
}
//如果非子节点的值小于左子节点和右子节点的值
if(arr[k]>temp){
//temp和arr[k]进行交换
arr[i] = arr[k];
i=k;//继续循环比较,假设k是左子节点,k+1是右子节点,然后引出公式
}else{
break;
}
}
//当for循环结束后,我们已经将以i为父节点的树的最大值,放在了最顶上(局部)
arr[i]=temp;
}