【算法】排序


前言

1. 排序的稳定性

对于一个排序,如果排序前a在b的前面,且排序对象,比如说a和b的分数是一样的,如果排序完a和b的顺序没有发生变化,则说明该排序是稳定排序,倘若发生了变化,则说明该排序是不稳定的

2. 内外排序

内排序是在整个排序过程中,待排序对象都放置在内存中。外排序则是由于数据过多要在内外存之间多次交换数据才能进行。
对于内排序而言,算法的性能主要受三方面影响:

  1. 时间性能
  2. 辅助空间
  3. 算法复杂度

3. 排序用到的结构和函数

typedef struct{
	int r[MAXSIZE+1];
	int length;
}SqList;

void swap(SqList &L, int i, int j){
	int temp = L.r[i];
	L.r[i] = L.r[j];
	L.r[j] = temp;
}

一、冒泡排序

冒泡排序的基本思想就是不断对比相邻两个数,如果左边的比右边的大,那我们就交换他们,做完一轮后,由于最小的一定以及出现在整个数组的左边了,这时我们只需要对剩下的元素继续进行冒泡排序就好,该算法的时间复杂度是O(n2)。

void BubbleSort(SqList &L){
	for(int i = 1; i <= L.length; ++i){
		for(int j = L.length-1; j >= i; ++j){
			if(L.r[j] > L.r[j+1]) swap(L, j, j+1);
		}
	}
}

如果出现后面所有数都已经拍好的情况,经典冒泡排序还是会跑完整个流程,这个时候我们引入一个flag,如果发现某一趟冒泡排序没有进行交换,那么flag就会被设置未FALSE并且退出循环。

void BubbleSort(SqList &L){
	bool flag = TRUE; 
	for(int i = 1; i <= L.length && flag; ++i){
		for(int j = L.length-1; j >= i; ++j){
		flag = FALSE:
			if(L.r[j] > L.r[j+1]) {
			swap(L, j, j+1);
			flag = TRUE;
			}
		}
	}

二、简单选择排序

简单插入排序的基本思想是不断遍历未排序部分,找出其中最小的值把他放到对应的位置上,算法复杂度是O(n2).

void SelectSort(SqList &L){
	for(int i = 0; i <= L.length; ++i){
		min = i;
		for(int j = i + 1; j <= L.length, ++j){
			if(L.r[min] > L.r[j]) min = j;
		}
		if(i != min) swap(L, i, min);
	}
}

三、直接插入排序

直接插入排序类似于我们玩扑克牌时,整理牌的时候,直接把他插入到它的位置,对于代码,从数组的第二个位置开始,依次向后做,如果当前位置的值小于左边的,就将他放到L.r[0],然后依次把前面的元素向后覆盖,直到找到他的位置,该算法的时间复杂度是O(n2)。

void InsertSort(SqList &L){
	for(int i = 2; i <= L.length; ++i){
		if(L.r[i] < L.r[i-1]){
			L.r[0] = L.r[i];
			for(int j = i -1; L.r[j] > L.r[0]; --j){
				L.r[j+1] = L.r[j];
			}
			L.r[j] = L.r[0];
		}
	}
}

四、希尔排序

希尔排序的核心思想是将数组分组,在每个组内进行直接插入排序,d是同组元素在数组中的间隔。

void ShellSort(SqList &L){
	int d = L.length;
	while(d >= 1){
		d /= 2;
		for(int i = d + 1; i <= L.length; ++i){
			if(L.r[i] < L.r[i-d]){
				L.r[0] = L.r[i];
				for(int j = i-d; j > 0 && L.r[0] < L.r[j]; j -= d)
				L.r[j+d] = L.r[j];
				L.r[j+d] = L.r[0]
			}
		}
	}
}

五、堆排序

堆排序主要使用完全二叉树,不断地找出未排序数组中最大的元素把他放到合适的位置上,代码详解看这里

void HeapHeadAdjust(SqList &L, int s, int m){
	// s是要存放根的位置,m是是待排序的元素个数
	int L.r[0] = L.r[s];
	for(int j = 2*s; j < m; j *= 2){
		if(L.r[j] < L.r[j+1]) ++j;	// j指向较大的元素
		if(L.r[0] >= L.r[j]) break;
		L.r[s] = L.r[j];
		s = j;
	}
	L.r[s] = L.r[0]
}
void HeapSort(SqList &L){
	for(int i = L.length / 2; i > 0; ++i)
		HeapHeadAdjust(L, i, L.length);
	for(int i = L.length; i > 1; ++i){
		swap(L, i, 1);
		HeapHeadAdjust(L, 1, i-1); 
	}
}

六、归并排序

归并排序首先将数组拆分成单个元素,再逐层往上合并,merge函数做的就是先将数组复制出去,然后逐项对比要合并的两组的大小,依次将小的数放回原来的数组,代码详解看这里

int temp[L.length];
void merge(SqList &L, int low, int mid, int high){
	for(int k = low; k < high; ++k){
		temp[k] = L[k];
	}
	for(int i = low, j = mid + 1, k = low; i <= mid && j <= high; ++k){
		if(temp[i] < temp[j]) L[k] = temp[i++];
		else L[k] = temp[j++];
	}
	while(i <= low) L[k++] = temp[i++];
	while(j <= high) L[k++] = temp[j++];
}
void MergeSort(SqList &L, int low, int high){
	int mid = 0;
	while(low < high){
		mid = (low + high) / 2;
		MergeSort(L, low, mid);
		MergeSort(L, mid + 1, high);
		merge(L, low, mid, high);
	}
}

七、快速排序

快速排序使用到了枢轴,首先我们先找到这个枢轴应该在的位置,通过两个游标一个在左一个在右,不断交换元素,确保枢轴在插入时左边所有元素都比他小,右边所有元素都比他大,代码详解看这里

int Partiton(SqList &L, int low, int high){
	int pivotkey = L.r[low];
	while(low < high){
		while(L.r[high] > pivotkey) high--;
		L.r[low] = L.r[high];
		while(L.r[low] < pivotkey) low--;
		L.r[high] = L.r[low];
	}
	L.r[low] = pivotkey;
	return low;
}
void QuickSort(SqList &L, int low, int high){
	if(low < high){
		int pivot = Partition(L, low, high);
		QuickSort(L, low, pivot-1);
		QuickSort(L, pivot+1, high);
	}
}

对于快速排序可以做出优化,优化小数组时的排序方案,快速排序不适合处理元素个数较少的数组,因而当数组元素个数少于一个阈值时,我们可以用直接插入排序来代替

void QuickSort(SqList &L, int low, int high){
	if((high - low) > MAX_LENGTH_INSERT_SORT){
		int pivot = Partition(L, low, high);
		QuickSort(L, low, pivot-1);
		QuickSort(L, pivot+1, high);
	}else
		InsertSort(L);
}

跳转系列文章目录


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值