八个排序算法

目录

1.冒泡排序

2.选择排序

3.插入排序

4.希尔排序

5.快速排序

6.堆排序

7.归并排序

8.计数排序


1.冒泡排序

思路:从N,N-1,···,2个数中找到最大值,放在当前数组的末尾。

优化版:当一趟比较中没有发生任何交换时,说明数组已经有序。

void bubbleSort(int arr[]) {
    int cnt = 0;
	for (int i = N; i > 0; --i) {
		for (int j = 0; j < i - 1; ++j) {
			if (arr[j] > arr[j + 1]) {
				SWAP(arr[j], arr[j + 1]);
    		    ++cnt;
            }
        if(cnt == 0)  return;
		}
	}
}

2.选择排序

① 从N,N-1,···,2个数中找到最小值;

② minPos记录了最小值的下标,找到minPos之后和未排好序部分最开始元素交换。

void selectSort(int arr[]) {
	for (int i = 0; i < N - 1; ++i) {
		int minPos = i;
		for (int j = i + 1; j < N; ++j) {
			if (arr[j] < arr[minPos]) {
				minPos = j;
			}
		}
		SWAP(arr[i], arr[minPos]);
	}
}

3.插入排序

思路:将未排序的元素拷贝一份,当成是新元素插入到原有序数组中;

① i表示新来元素下标;j表示探查元素的下标;

② 如果arr[ j ] > arr[ i ],后移;反之则插入到arr[ j ]后面。

void insertSort(int arr[]) {
	for (int i = 1; i < N; ++i) {
		int insertVal = arr[i];
		int j;
        //找到插入位置后插入,然后退出循环,所以在循环条件中直接加入arr[j] > insertVal
		for (j = i - 1; j >= 0 && arr[j] > insertVal; --j) {
			arr[j + 1] = arr[j];
		}
		arr[j + 1] = insertVal;
	}
}

4.希尔排序

思路:改变插入排序中的步长,将数组分组排序,每趟趟得到比原来更有序的数组

原因:插入排序下,原数据越有序,速度越快

① 将步长d设置为N/2,把数组分为N/2个组排序;

② 将步长设置为d/2,···

③ 知道步长为1时,变为插入排序。

void shellSort(int arr[]) {
	for (int d = N / 2; d > 0; d >>= 1) {
		for (int i = d; i < N; i+=d) {
			int insertVal = arr[i];
			int j;
			for (j = i - d; j >= 0 && arr[j] > insertVal; j -= d) {
				arr[j + d] = arr[j];
			}
			arr[j + d] = insertVal;
		}
	}
}

5.快速排序

递归版:分治+递归

① 找到一个枢纽元素pivot,把比pivot小的元素放在左边,把比pivot大的元素放在右边;

② 递归排序左边,递归排序右边;

③ 当只有0或1个元素时,数组天然有序。

注意:

① 当数组中元素个数大于等于二才有排序的必要;

② 考虑两种极端情况:所有的数都大于/小于pivot。

int partition(int arr[], int left, int right) {
	int i, j;
	for (i = left, j = left; i < right; ++i) {
		//i:探查比pivot小的值换到左边 j:比pivot大的第一个值
		if (arr[i] < arr[right]) {
			SWAP(arr[i], arr[j]);
			++j;
		}
	}
	SWAP(arr[j], arr[right]);
	return j;
}

void quickSort(int arr[], int left, int right) {
	if (left < right) {                          //缺少条件会导致溢出
		int pivot = partition(arr, left, right);
		quickSort(arr, left, pivot - 1);
		quickSort(arr, pivot + 1, right);
	}
}

非递归版:qsort

#include <stdlib>

void qsort(void *buf, size_t num, size_t size, int (*compare)(const void*, const void*));

① num 数组中元素个数;

② size 元素大小;

③ int (*compare)(const void*, const void*) 函数指针,回调函数;

④ const void* 将参数强制转换为以元素类型为基类型的指针。

//自定义比较函数
int MyCompare(const void* p1, const void* p2) {
	int* pLeft = (int *)p1;
	int* pRight = (int*)p2;
	return *pLeft - *pRight;
}
//调用qsort
qsort(arr, N, sizeof(int), MyCompare);

6.堆排序

① 对N个无序元素建大根堆。

    自底向下,从最后一个双亲结点向前遍历直到根,按照兄弟结点,双亲结点的顺序比较,找到最大元素放在根。

② 调整堆。

    将根和最后一个结点交换,此时的最后一个结点脱离堆,堆的规模-1。从根出发重新调整为大根堆。

③ 重复②直到堆的规模为1。

void adjustMaxHeap(int arr[], int pos, int len) {
	//pos 要调整的结点;len 堆的当前规模
	//pos左孩子=2*(pos+1) 下标 = 2*pos+1
	//pos右孩子下标 = 2*pos+2
	int parent = pos;
	int child = 2 * pos + 1;
	//保证孩子在堆内
	while (child < len) {
		if (child + 1 < len && arr[child] < arr[child + 1]) {     //兄弟比较
			++child;
		}
		if (arr[child] > arr[parent]) {                           //孩子和双亲比较
			SWAP(arr[child], arr[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else {
			break;
		}
	}
}

void heapSort(int arr[]) {
	//从最后一个双亲结点自底向上建立大根堆
	for (int i = N / 2 - 1; i >= 0; --i) {
		adjustMaxHeap(arr, i, N);
	}
	//交换堆顶和末尾元素,堆规模-1
	SWAP(arr[0], arr[N - 1]);
	//自顶向下调整大根堆
	for (int i = N - 1; i >= 2; --i) {
		adjustMaxHeap(arr, 0, i);
		SWAP(arr[0], arr[i - 1]);
	}
}

7.归并排序

思路:分治 + 递归

① 先排序左边,再排序右边,最后合并左右两边;

② 当数组中只有0/1个元素时自然有序。

排序方法:将原来的数据从arr拷贝到brr,用i来遍历左边,j遍历右边,k指向结果。

                  取brr[i]和brr[j]中较小的作为结果,指针后移。

                  当有一边遍历结束,直接把另一边剩余部分加入。

注意:当left=right时排序结束。

void merge(int arr[], int brr[], int left, int mid, int right) {
	memcpy(brr + left, arr + left, (right - left + 1) * sizeof(int));
		//i指向左半边,j指向右半边,k指向结果
	int i, j, k;
	for (i = left, j = mid + 1, k = left; i <= mid && j <= right; ++k) {
		if (brr[i] < brr[j]) {
			arr[k] = brr[i];
			++i;
		}
		else {
			arr[k] = brr[j];
			++j;
		}
	}
	while (i <= mid) {
		arr[k++] = brr[i];
		++i;
	}
	while (j <= right) {
		arr[k++] = brr[j];
		++j;
	}
}

void mergeSort(int arr[], int brr[], int left, int right) {
	if (left < right) {
		int mid = (left + right) / 2;
		mergeSort(arr, brr, left, mid);
		mergeSort(arr, brr, mid + 1, right);
		merge(arr, brr, left, mid, right);
	}
}

8.计数排序

① 创建一个辅助数组count[M]并初始化;

② 遍历arr数组,根据arr[i]修改count数组下标为arr[i]的值;

③ 从头到尾遍历count,重建arr数组。

void countSort(int arr[]) {
	int count[M] = { 0 };
	for (int i = 0; i < N; ++i) {
		++count[arr[i]];
	}
	int k = 0;
	for (int j = 0; j < M; ++j) {
		while (count[j]-- > 0) {
			arr[k++] = j;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值