常见排序代码及手撕心得

博主学习历程:

首先,思想不难,代码更是简单,仅仅是非递归快排和循环归并略有难度(个人认为难度集中在边界条件上)

学习方法:这里博主是听老师细细讲解(各种排序思想,老师演示手撕和注意事项,顺带学习手撕技巧和各种排序的价值和历史),

        理解思想和技巧,课上讲完之后,完全可以时隔半周仍能按思路手撕,第一次手撕可能略有缓慢,切记不要行照抄,比较找错之事,那样不如不学!!

手撕技巧:

        1.请脑子里清楚知道排序的思想,是怎样把数组里的值排序的。这都不知道的话,根本手撕不了一点。

        2.博主理解里,排序可分两种。一是,可以看成几个步骤,如:计数排序等,这种排序简单,直直按着思想写就行;二是,循环套循环一样的排序,如:希尔排序,归并排序等,这种一般操作(第一次手撕时)是根据先写单趟(单次,或者说是排“第一个”),然后再套个循环把数组每个排,然后再套——总之是先完成"每一轮"的,再产生每一轮的初始条件(套循环)

        3.相信自己,自己手撕的时候,别别别别别别看别人的代码,全程自己写,出错请使用调试+打印小技巧。

代码:

堆排用到了向下调整,非递归快排用到了栈结构,以int类型为例

栈stack.h

#pragma once
#include<stdlib.h>
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;
// 初始化栈 
void StackInit(Stack* ps);
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);

stack.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
#include<assert.h>
// 初始化栈 栈顶代表的下标存数据
void StackInit(Stack* ps)
{
	assert(ps);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = -1;
}
// 入栈 
void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	if (ps->_capacity == (ps->_top + 1)) {
		ps->_capacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
		STDataType* ptmp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * ps->_capacity);
		assert(ptmp);
		ps->_a = ptmp;
	}
	++(ps->_top);
	ps->_a[ps->_top] = data;

}
// 出栈 
void StackPop(Stack* ps)
{
	assert(ps);
	if (ps->_top > -1) {
		--(ps->_top);
	}
}
// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
	assert(ps);
	if (ps->_top > -1) {
		return ps->_a[ps->_top];
	}
}
// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top + 1;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
	assert(ps);
	if (ps->_top == -1) {
		return 1;
	}
	else {
		return 0;
	}
}
// 销毁栈 
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = -1;
}

sort.h

#pragma once
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
// 选择排序
void SelectSort(int* a, int n);


// 堆排序
void AdjustDwon(int* a, int n, int root);
void HeapSort(int* a, int n);

// 插入排序
void InsertSort(int* a, int n);
// 希尔排序
void ShellSort(int* a, int n);

// 冒泡排序
void BubbleSort(int* a, int n);
// 快排非递归
void QuickSortNonR(int* a, int n);

// 归并
void MergeSort(int* a, int n);

//计数,hash
void CountSort(int* a, int n);

sort.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"sort.h"
#include"stack.h"
//两个指针,先用int做测试;
int SortCmp(int* p1, int* p2)//p1大于p2 为升序
{
	assert(p1);
	assert(p2);
	if (*p1 > *p2) return 1;
	else return 0;
}
void Swap(int* p1, int* p2)
{
	assert(p1);
	assert(p2);
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

// 选择排序
void SelectSort(int* a, int n)
{
	assert(a);

	int left = 0;
	int right = n - 1;
	while (left < right) {
		int tmp = left;//记录交换下标
		for (int i = left; i <= right; ++i) {
			if (!SortCmp(&a[i], &a[tmp])) {
				tmp = i;
			}
		}
		Swap(&a[tmp], &a[left]);
		++left;
	}
}
// 堆排序
void AdjustDwon(int* a, int n, int root)
{
	assert(a);
	int father = root;
	int swapSon = father * 2 + 1;
	while (swapSon < n)
	{
		if (swapSon + 1 < n && SortCmp(&a[swapSon + 1], &a[swapSon])) {
			++swapSon;
		}
		if (SortCmp(&a[swapSon], &a[father])) {
			Swap(&a[swapSon], &a[father]);
			father = swapSon;
			swapSon = father * 2 + 1;
		}
		else {
			break;
		}

	}

}
void HeapSort(int* a, int n)
{
	assert(a);

	int father = (n - 1 - 1) / 2;
	while (father >= 0) {

		AdjustDwon(a, n, father);
		--father;
	}
	while (n != 1) {
		Swap(&a[0], &a[n - 1]);
		--n;
		AdjustDwon(a, n, 0);//每次从top开始向下check
	}
}


// 插入排序
void InsertSort(int* a, int n)
{
	assert(a);

	//先搞个升序
	//单次
	int spacer = 1;
	for (int i = 0; i < n - spacer; ++i) {
		int end = i;//单次范围下标_right
		int tmp = a[end + spacer];
		while (end >= 0) {
			if (SortCmp(&tmp, &a[end])) {
				break;
			}
			a[end + spacer] = a[end];
			end -= spacer;
		}
		a[end + spacer] = tmp;
	}

}
// 希尔排序
void ShellSort(int* a, int n)
{
	assert(a);
	int spacer = n;
	while (spacer > 1) {//预排序到最后一次排序的循环
		spacer = spacer / 3 + 1;
		for (int i = 0; i < n - spacer; ++i) {//从下标0到这一次排序末端的一次排序或预排序
			int end = i;//单次范围下标_right
			int tmp = a[end + spacer];
			while (end >= 0) {//一组的循环
				if (SortCmp(&tmp, &a[end])) {//比较
					break;
				}
				a[end + spacer] = a[end];
				end -= spacer;
			}
			a[end + spacer] = tmp;
		}
	}
}


// 冒泡排序
void BubbleSort(int* a, int n)
{
	assert(a);
	for (int i = n - 1; i > 0; --i) {
		for (int j = 0; j < i; ++j) {
			if (SortCmp(&a[j], &a[j + 1]) ){
				Swap(&a[j], &a[j + 1]);
			}
		}
	}

}
//快排,非递归,栈实现
void QuickSortNonR(int* a, int n)
{
	assert(a);
	srand((size_t)time(NULL));
	int left = 0, right = n - 1;

	Stack tmp;
	StackInit(&tmp);
	StackPush(&tmp, left);
	StackPush(&tmp, right);

	while (!StackEmpty(&tmp)) {
		//取元素
		right = StackTop(&tmp);
		StackPop(&tmp);
		left = StackTop(&tmp);
		StackPop(&tmp);

		int begin = left;
		int end = right;
		int mid = rand() % (end - begin + 1);//随机数做比较值

		/*printf("mid = %d \n", mid);
		Print(a, left, right);*/

		Swap(&a[left + mid], &a[left]);
		while (begin < end) {
			while (begin < end && SortCmp(&a[end], &a[left])) {//等于的在左边
				--end;
			}
			while (begin < end && !SortCmp(&a[begin], &a[left])) {
				++begin;
			}
			Swap(&a[begin], &a[end]);
		}
		Swap(&a[left], &a[end]);
		//mid = end;//分割数组
		//[left, mid - 1][mid + 1, right]
		if (left < end - 1) {
			StackPush(&tmp, left);
			StackPush(&tmp, end - 1);
		}
		if (end + 1 < right) {
			StackPush(&tmp, end + 1);
			StackPush(&tmp, right);
		}
	}
	StackDestroy(&tmp);
}

//归并,循环实现,二路归并
void MergeSort(int* a, int n)
{
	int gap = 1;
	int* atmp = (int*)malloc(sizeof(int) * n);

	int i = 0;
	while (gap < n) {
		i = 0;
		//[left1, right1 - 1][left2, right2 - 1]
		int left1 = 0, right1 = gap;
		int left2 = gap, right2 = gap + gap;
		if (right2 > n) {
			right2 = n;
		}
		while (left1 != n) {//每行的循环
			//printf("%d ", left1);
			while (left1 < right1 && left2 < right2) {//每两个小区间的循环
				if (!SortCmp(&a[left1], &a[left2])) {
					atmp[i++] = a[left1];
					++left1;
				}
				else {
					atmp[i++] = a[left2];
					++left2;
				}
			}
			while (left1 < right1) {
				atmp[i++] = a[left1++];
			}
			while (left2 < right2) {
				atmp[i++] = a[left2++];
			}
			left1 = right2;
			right1 += gap * 2;
			left2 = right1;
			right2 += gap * 2;
			if (right1 > n) {
				right1 = n;
				left2 = n;
			}
			if (right2 > n) {
				right2 = n;
			}
		}
		memmove(a, atmp, sizeof(int) * n);

		gap *= 2;
	}
}

//计数,hash
void CountSort(int* a, int n)
{
	//先找到数组里的最大,最小值,以确定数据范围
	int max = a[0];
	int min = a[0];
	for (int i = 0; i < n; ++i) {
		if (a[i] > max) {
			max = a[i];
		}
		if (a[i] < min) {
			min = a[i];
		}
	}
	int* ptmp = (int*)calloc((max - min + 1), sizeof(int) );
	if (ptmp == NULL) {
		perror("ptmp:malloc");
		exit(1);
	}
	for (int i = 0; i < n; ++i) {
		ptmp[a[i] - min]++;
	}
	int cur = 0;
	for (int i = 0; i < (max - min + 1); ++i) {
		while (ptmp[i]) {
			a[cur++] = i + min;
			--ptmp[i];
		}
	}
}

sortTest.c

//简单的测试用例

#define _CRT_SECURE_NO_WARNINGS 1

#include"sort.h"
void print(int* a, int n)
{
	for (int i = 0; i < n; ++i) {
		printf("%d ", a[i]);
	}
	printf("\n");
}
void test01()
{
	int a1[] = { 3, 5, 8, 1, 2, 6, 9, 7, 0, 4 };
	//int a1[] = { 2, 7, 1, 6 , 3 , 9, 5, 4, 0, 8, 51, 20, 11, -11, 2,33 ,3,4,6,7 };
	int a2[] = { 2, 7, 1, 6 , 3 , 9, 5, 4, 0, 8, 51, 20, 11, -11, 2,33 ,3,4,6,7 };

	//SelectSort(a1, sizeof(a1) / sizeof(int));
	//HeapSort(a2, sizeof(a1) / sizeof(int));
	//InsertSort(a1, sizeof(a1) / sizeof(int));
	//ShellSort(a1, sizeof(a1) / sizeof(int));
	//BubbleSort(a1, sizeof(a1) / sizeof(int));
	//QuickSortNonR(a1, sizeof(a1) / sizeof(int));
	//print(a1, sizeof(a1) / sizeof(int));
	//print(a2, sizeof(a1) / sizeof(int));
	//MergeSort(a1, sizeof(a1) / sizeof(int));
	CountSort(a1, sizeof(a1) / sizeof(int));
	//printf("\n");
	print(a1, sizeof(a1) / sizeof(int));

}
int main()
{
	test01();
	return 0;
}

相关推荐

  1. 常见排序代码心得

    2024-03-29 03:14:02       23 阅读
  2. 深度学习代码

    2024-03-29 03:14:02       7 阅读

最近更新

  1. 大模型/NLP/算法面试题总结4——bert参数量计算

    2024-03-29 03:14:02       0 阅读
  2. springsecurity(学习自用)

    2024-03-29 03:14:02       0 阅读
  3. 构建响应式CSS导航栏:实现优雅的用户体验

    2024-03-29 03:14:02       0 阅读
  4. debian或Ubuntu中开启ssh允许root远程ssh登录的方法

    2024-03-29 03:14:02       0 阅读
  5. 深入理解基本数据结构:链表详解

    2024-03-29 03:14:02       0 阅读
  6. 白骑士的C++教学基础篇 1.3 控制流

    2024-03-29 03:14:02       1 阅读
  7. Istio实战教程:Service Mesh部署与流量管理

    2024-03-29 03:14:02       1 阅读

热门阅读

  1. 独立服务器和云计算各有什么优势

    2024-03-29 03:14:02       16 阅读
  2. 测试学习1

    2024-03-29 03:14:02       22 阅读
  3. 15届蓝桥杯备赛(3)

    2024-03-29 03:14:02       23 阅读
  4. 如何在Tomcat 9上部署前端和后端项目

    2024-03-29 03:14:02       24 阅读
  5. 题目 2833: 金币

    2024-03-29 03:14:02       20 阅读
  6. go | map、multiple returnvalues、variadic function、recursion

    2024-03-29 03:14:02       19 阅读
  7. 什么是jQuery?

    2024-03-29 03:14:02       18 阅读
  8. 全球变暖(dfs和bfs)

    2024-03-29 03:14:02       18 阅读
  9. 数据结构与算法-分治算法

    2024-03-29 03:14:02       18 阅读
  10. CAS中的ABA问题

    2024-03-29 03:14:02       18 阅读
  11. linux -- sysctl详解1

    2024-03-29 03:14:02       18 阅读