算法基础-排序

1 选择排序

给N个数,第一个小的放在第一个位置,第二个小的放在第二个位置就是选择排序了

public static void selectionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length - 1; i++) { 
			int minIndex = i;
			for (int j = i + 1; j < arr.length; j++) {
				if(arr[j] < arr[minIndex]) {
					minIndex = j;
				}
			}
			swap(arr, i, minIndex);
		}
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

2冒泡排序

给N个数,从左到右,谁大谁往后,那么一次遍历后,最大值一定在最后一个位置,以此类推,排序即可

public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int end = arr.length - 1; end > 0; end--) {
			for (int i = 0; i < end; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);
				}
			}
		}
	}


	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

3插入排序

给N个数 从0-0,0-1,0-2,0-3。。范围依次去处理变为升序即可

假如 43125      

0-0范围  4不变

0-1范围 变为34

0-2范围  1看到4大变为314,1看到3大变为 134, 以此类推

public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 1; i < arr.length; i++) {
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
				swap(arr, j, j + 1);
			}
		}
	}

	
	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

4归并排序

归并:顾名思义,有点分治的意思,比如你日常开发,数据大,你需要多线程处理,然后一块给你返回汇总,归并排序就是以二分为基础,先搞左边的,再搞右边的,最后搞一块就行

比如  3421 拆成 34 和21

左边还是34 右边变为12

然后左右都有一个指针先指向3和1 右边小则输出1移动右指针,还小输出2移动右指针为null,左指针从左到右依次输出34,最终排序为1234

// 递归方法实现
	public static void mergeSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		process(arr, 0, arr.length - 1);
	}

	// arr[L...R]范围上,请让这个范围上的数,有序!
	public static void process(int[] arr, int L, int R) {
		//L和R相等已经二分到了极致,不需要任何操作了
		if (L == R) {
			return;
		}
		// int mid = (L + R) / 2 二分找到中点
		int mid = L + ((R - L) >> 1);
		//找到左边的按照顺序去排序
		process(arr, L, mid);
		//找到右边的按照顺序去排序
		process(arr, mid + 1, R);
		//合并左右
		merge(arr, L, mid, R);
	}
	//arr 数组,L数组的左边界,M数组的中间+1的数,R右边界
	public static void merge(int[] arr, int L, int M, int R) {
		//存储答案
		int[] help = new int[R - L + 1];
		//计数
		int i = 0;
		//左边开始位置指针
		int left = L;
		//右边开始位置指针
		int  right= M + 1;
		//左右指针 谁小谁赋值
		while (left <= M && right <= R) {
			help[i++] = arr[left] <= arr[right] ? arr[left++] : arr[right++];
		}
		// 要么left越界,要么right越界
		// 不可能出现:共同越界   将没有越界的赋值
		while (left <= M) {
			help[i++] = arr[left++];
		}
		while (right <= R) {
			help[i++] = arr[right++];
		}
		//放回arr
		for (i = 0; i < help.length; i++) {
			arr[L + i] = help[i];
		}
	}

5快排(荷兰国旗)

先了解

给N个数排序,先找到最后一个数X,右区间把最后一个数扩进去,小于等于X放左边,大于X放右边,等于放中间遍历满足条件

1.当前值<X,当前值和<=区的下一个数交换,<=区间右扩,当前值右移

2.当前值>X,当前值不动,>=区左边的数和当前值交换,>=区左扩

3等于当前值 右移

代码中 swap(arr, L + (int) (Math.random() * (R - L + 1)), R);这个就是随机打基准值,不是取最右边的数,是随机取放在最右边,这样复杂度会从0N2变为 NlogN

public static int[] partition(int[] arr, int L, int R) {
		int lessR = L - 1;
		int moreL = R;
		int index = L;
		while (index < moreL) {
			if (arr[index] < arr[R]) {
				swap(arr, ++lessR, index++);
			} else if (arr[index] > arr[R]) {
				swap(arr, --moreL, index);
			} else {
				index++;
			}
		}
		//把最后边的值归位
		swap(arr, moreL, R);
        //返回相等的区间
		return new int[] { lessR + 1, moreL };
	}
public static void process(int[] arr, int L, int R) {
		if (L >= R) {
			return;
		}

        swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
		int[] equalE = partition(arr, L, R);
         //从相等区间的左边和右边继续排序去
		process(arr, L, equalE[0] - 1);
		process(arr, equalE[1] + 1, R);
	}
public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

桶排序

桶排序思想下的排序:计数排序 & 基数排序

1)桶排序思想下的排序都是不基于比较的排序

2)时间复杂度为O(N),额外空间负载度O(M)

3)应用范围有限,需要样本的数据状况满足桶的划分

4)一般来讲,计数排序要求,样本是整数,且范围比较窄

5)一般来讲,基数排序要求,样本是10进制的正整数

算法思路1:

假如有 101 201 105    055 这几个数  十进制的数默认准备 10个桶子,

先装个位数的

---------------------------------------------------------------

0号桶 

1号桶   101 201

2号桶 

5号桶 105 055  弹出

101 201 105 055 

------------------------------------------------------

 十位数进

0号桶 101 201 105

1号桶   

2号桶 

5号桶 055  弹出

101 201 105 055

------------------------------------------------------------------------

百位数进

0号桶 055

1号桶   101 105

2号桶  201

 弹出

055 101  105  201 排好序了

算法思路2:

假如有 101 201 105    055 这几个数  十进制的数默认准备 int[10]

先装个位数的(从左到右)

---------------------------------------------------------------

int[1]=2  int[5]=2

然后准备一个int[10]' 计算前缀和

Int[0]=0 Int[1]=2 Int[2]=2...int[5]=7 这个的含义就是,

当我们从右往左遍历的时候

当你的个位数的值是X的时候

那么你在arr的位置[用help[]代替]应该安排在help[int[X]-1]这个位置然后把int[X]--

也就是说int前缀和代表你这个值是在help的从0到X的位置,因为你从右往左遍历所以放在最后一个位置。

------------------------------------------------------

 十位数进 百位数进 雷同 我们就是用int[]替代了桶子,因为桶子可能会建立多个队列

public static void radixSort(int[] arr, int L, int R, int digit) {
		final int radix = 10;
		// 有多少个数准备多少个辅助空间
		int[] help = new int[R - L + 1];
		//遍历N位数 个位/十位/百位
		for (int i = 1; i< digit; i++) {
			int[] count=new int[radix];
			for (int j = L; j<= R; j++) {
				count[getDigit(arr[j], i)]++;
			}
			//获取前缀和
			for (int z = 1; z < count.length; z++) {
				count[z]=count[z-1]+count[z];
			}
			//从右往左遍历
			for (int x = R; x >= L; x--) {
				int digit1 = getDigit(arr[x], i);
				help[count[digit1]-1]=arr[x];
				count[digit1]--;
			}
			//给之前的arr[]赋值
			for (int a = L, b = 0; a <= R; a++, b++) {
				arr[a] = help[b];
			}
		}
	}
public static int getDigit(int x, int d) {
		return ((x / ((int) Math.pow(10, d - 1))) % 10);
	}
public static void radixSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		radixSort(arr, 0, arr.length - 1, maxbits(arr));
	}

	public static int maxbits(int[] arr) {
		int max = Integer.MIN_VALUE;
		for (int i = 0; i < arr.length; i++) {
			max = Math.max(max, arr[i]);
		}
		int res = 0;
		while (max != 0) {
			res++;
			max /= 10;
		}
		return res;
	}

总结

 

1)不基于比较的排序,对样本数据有严格要求,不易改写

2)基于比较的排序,只要规定好两个样本怎么比大小就可以直接复用

3)基于比较的排序,时间复杂度的极限是O(N*logN)

4)时间复杂度O(N*logN)、额外空间复杂度低于O(N)、且稳定的基于比较的排序是不存在的。

5)为了绝对的速度选快排、为了省空间选堆排、为了稳定性选归并

6)归并排序的额外空间复杂度可以变成O(1),“归并排序 内部缓存法”,但是将变得不再稳定。

7)“原地归并排序" ,会让时间复杂度变成O(N^2)

8)快速排序稳定性改进,“01 stable sort”,但是会对样本数据要求更多。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

普朗克的朗姆酒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值