常用的排序算法

1. 冒泡排序

算法思路:

第一趟,通过两两交换的手段,将最大元素顶到最末端

第二趟,将次大元素顶到倒数第二个位置

…………………………………………

时间复杂度:O(n²)

空间复杂度:O(1)

原址排序

稳定性:有相同元素,排序前和排序后相对位置不会变化,稳定

代码:

package 排序;

/**
 * @author: DreamCode
 * @file: 冒泡排序.java
 * @time: 2022-4-8-15:26:06
 */
public class 冒泡排序 {

	public static void main(String[] args) {
		int[] arr = { 3, 5, 7, 1, 2, 0, 2, 1, 5, 10, 22, 6 };
		BubbleSort(arr);
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
	}

	private static void BubbleSort(int[] arr) {
		// TODO 冒泡排序
		int len = arr.length;
		for (int i = 0; i < len; i++) {
			for (int j = 0; j < len - i - 1; j++) {   //注意上边界,泡都从第一个开始冒,但是每一趟完成之后,天花板在降低
				if (arr[j] > arr[j + 1]) {
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}

	}

}

2. 选择排序

算法思路:

思路:第一趟,选择所有元素中最小的,和第一位交换

第二趟,选择第二位及以后所有元素中最小的,和第二位交换

……

时间复杂度:O(n²)

空间复杂度:O(1)

原址排序

稳定性:考虑32211,第一趟标记最末1为最小→1(4)221(3)3;第二趟标记原下标3的1为最小→1(4)1(3)223

两个1的相对位置发生了变化,因此不稳定

代码:

package 排序;

/**
 * @author: DreamCode
 * @file: 选择排序.java
 * @time: 2022-4-8-15:39:50
 */
public class 选择排序 {

	public static void main(String[] args) {
		int[] arr = { 3, 5, 7, 1, 2, 0, 2, 1, 5, 10, 22, 6 };
		SellectionSort(arr);
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}

	}

	private static void SellectionSort(int[] arr) {
		// TODO 选择排序
		int len = arr.length;
		for (int i = 0; i < len; i++) {
			int minIndex = i;
			for (int j = i; j < len; j++) { // 从i~len中找到最小的数放在第i位置
				if (arr[j] < arr[minIndex]) { // 当前位置数比最小的数还小,更新
					minIndex = j;
				}
			}
			int temp = arr[minIndex]; // 交换
			arr[minIndex] = arr[i];
			arr[i] = temp;
		}

	}

}

3. 插入排序

算法思路:

假设某元素之前的子序列有序,该元素如果大于子序列末端元素则可继续下一个元素;如果该元素小于子序列末端,则往前插入到指定位置

第一趟:第一个元素已经有序
第二趟:第二个元素往前插
第三趟:第三个元素往前两个元素插
……………………………………

时间复杂度:O(n²)=1+2+3+…+n-1

空间复杂度:O(1)

原址排序

稳定性:由于是从后往前,后续元素如果存在前面相等的,无法越过,相对位置不会发生变化

代码:

package 排序;

/**
 * @author: DreamCode
 * @file: 插入排序.java
 * @time: 2022-4-8-15:47:58
 */
public class 插入排序 {

	public static void main(String[] args) {
		int[] arr = { 3, 5, 7, 1, 2, 0, 2, 1, 5, 10, 22, 6 };
		InsertionSort(arr);
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
	}

	private static void InsertionSort(int[] arr) {
		for (int i = 1; i < arr.length; i++) {
			int temp = arr[i];  // 保存每次需要插入的那个数
			int j;
			for (j = i; j > 0 && arr[j - 1] > temp; j--) {  // 寻找当前数可以插的位置
				arr[j] = arr[j - 1];  // 把大于需要插入的数往后移动。最后不大于temp的数就空出来j
			}
			arr[j] = temp;  // 将需要插入的数放入这个位置
		}

	}

}

4. 堆排序

算法描述:

首先要知道大顶堆和小顶堆,数组就是一个堆,每个i节点的左右孩子是2i+1和2i+2

有了堆,将其堆化:从n/2-1个元素开始向下修复,将每个节点修复为小(大)顶堆

修复完成后,数组具有小(大)顶堆的性质

按序输出:小顶堆可以对数组逆序排序,每次交换栈顶和末尾元素,对栈顶进行向下修复,这样次小元素又到堆顶了

时间复杂度: 堆化:一半的元素修复,修复是单分支的,所以整体堆化为nlgn/2

排序:n个元素都要取出,因此调整n次,每次调整修复同上是lgn的,整体为nlgn 空间复杂度:不需要开辟辅助空间

原址排序

稳定性:

代码:

package 堆排序;

/**
 * @author: DreamCode
 * @file: 堆排序.java
 * @time: 2022年3月7日-上午8:34:39
 */
public class 堆排序 {

	public static void main(String[] args) {
		int[] arr = { 1, 3, 5, 2, 6, 4, 8, 1 };
		sort(arr);
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
	}

	private static void sort(int[] arr) {
		// TODO 堆排序
		int n = arr.length;
		// 进行堆化
		MinHeap(arr);
		// 进行排序
		for (int i = n - 1; i >= 0; i--) {  //每次交换堆顶和末尾元素,对栈顶进行向下修复,这样次小元素又到堆顶了
			swap(arr, 0, i);
			MinHeapFixDown(arr, 0, i - 1);  //每一次能确定最后一个元素,这里每一轮能确定一个最大值
		}

	}

	private static void MinHeap(int[] arr) {
		// TODO 进行堆化
		int n = arr.length;
		for (int i = n / 2 - 1; i >= 0; i--) {  
			// TODO 实现每一个数都是堆
			MinHeapFixDown(arr, i, n);  //堆修复,节点下沉
		}

	}

	private static void MinHeapFixDown(int[] arr, int i, int n) {
		// TODO 节点下沉
		// 判断是否越界
		int left = 2 * i;
		int right = 2 * i + 1;
		if (left >= n) {   // 左节点越界,没有左右孩子,当前堆只有一个元素,即为叶子节点
			return;
		}
		// 获取左右节点中的最小值
		int min = left;
		if (right >= n) {  // 右节点越界
			min = left;
			return;
		} else {
			min = arr[left] <= arr[right] ? left : right;  
		}
		if (arr[i] > arr[min]) {
			// 将节点与最小值交换,递归进行节点下沉
			swap(arr, i, min);
			MinHeapFixDown(arr, min, n);
		}

	}

	private static void swap(int[] arr, int i, int min) {
		// TODO 两数交换
		int temp = arr[i];
		arr[i] = arr[min];
		arr[min] = temp;
	}

}

PriorityQueue为最优队列容器类,里面维护的即一个堆

4. TopK问题

问题描述:

求海量数据(正整数)按逆序排列的前k个数(topK),因为数据量太大,不能全部存储在内存中,只能一个一个地从磁盘或者网络上读取数据,

请设计一个高效的算法来解决这个问题 第一行:用户输入K,代表要求得topK 随后的N(不限制)行,每一行是一个整数代表用户输入的数据

用户输入-1代表输入终止 请输出topK,从小到大,空格分割

思路解析:

堆排序的应用,小根堆每一轮排序能确定一个最大的值

package 排序;

import java.util.Scanner;

/**
 * @author: DreamCode
 * @file: TopK.java
 * @time: 2022年3月7日-下午2:08:09
 */
public class TopK {

	static int k;
	static int[] heap;
	static int index;  //记录当前为第几个top

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		k = scanner.nextInt();
		heap = new int[k];
		int x = scanner.nextInt();
		while (x != -1) {
			deal(x);
			x = scanner.nextInt();
		}

	}

	private static void deal(int x) {
		if (index < k) {  //当前的数目小于k个数
			heap[index++] = x;
			if (index == k) {  //等于k个数,直接堆化
				MinHeap(); // 堆化
			}
		} else {  //再次加入新的数,此时直接跟堆顶比较,因为堆顶为最小的值,所以只有比堆顶大才有机会进入前topK个数
			if (heap[0] < x) {
				heap[0] = x;  //更新堆顶
				MinHeapFixDown(heap,0, k);  //重新修复堆
			}
			printHeap();
		}

	}

	private static void printHeap() {
		// TODO 打印堆
		int[] heapCopy = new int[k]; 
		System.arraycopy(heap, 0, heapCopy, 0, k);
		sort(heapCopy);  //对堆排序
		for (int i = 0; i < k; i++) {
			System.out.print(heapCopy[i] + " ");
		}
		System.out.println();
		for (int i = 0; i < k; i++) {
			System.out.print(heap[i] + " ");
		}

	}

	private static void sort(int[] heapCopy) {
		// TODO 堆排序
		int n = heapCopy.length;
		for(int i=n-1;i>=0;i--) {
			swap(heapCopy,0, i);
			MinHeapFixDown(heapCopy,0, i-1);
		}
		
	}

	private static void MinHeap() {
		// TODO 对heap进行堆化
		int n = heap.length;
		for (int i = n / 2 - 1; i >= 0; i--) {
			MinHeapFixDown(heap,i, n);
		}

	}

	private static void MinHeapFixDown(int []arr,int i, int n) {
		// TODO 堆节点下沉
		int left = 2 * i;
		int right = 2 * i + 1;
		int min = left;
		if (left >= n) { // 当前节点为叶子节点
			return;
		} else {
			if (right < n) { // 左右儿子均存在
				min = arr[left] > arr[right] ? right : left;
			}
		}
		if (arr[i] > arr[min]) {
			swap(arr,i, min); // 与最小值进行交换
			MinHeapFixDown(arr,min, n);
		}

	}

	private static void swap(int[]arr,int i, int min) {
		// TODO 交换两数
		int temp = arr[i];
		arr[i] = arr[min];
		arr[min] = temp;

	}
}

5. 排序数组中找指定和的因子

问题描述:

在一个排序数组中,找指定和的两个因子

思路解析:

双指针遍历

package 排序;

/**
 * @author: DreamCode
 * @file: 排序数组中找和的因子.java
 * @time: 2022年3月7日-上午10:23:06
 */
public class 排序数组中找和的因子 {


	public static void main(String[] args) {
		int[] arr = {-8,-4,0,2,4,5,8,9,10};
		int k=10;
		int left=0,right=arr.length-1;
		while(left<right) {
			if(arr[left]+arr[right]>k) {
				right--;
			}else if (arr[left]+arr[right]<k) {
				left++;
			}else {
				System.out.println(arr[left]+" "+arr[right]);
				left++;
			}
		}

	}

}

6. 判断数组的包含问题

问题描述:

判断字符串A中的字符是否全部出现在字符串B中

思路解析:

将字符串A转化为字符数组,采用二分搜索算法,判断A中的每一个字符是否存在于B中

package 排序;

import java.util.Arrays;

/**
 * @author: DreamCode
 * @file: 判断数组的包含问题.java
 * @time: 2022年3月7日-下午9:22:15
 */
public class 判断数组的包含问题 {

	private static boolean check(String s1, String s2) {
		char[] s2_char = s2.toCharArray();
		Arrays.sort(s2_char);
		for(int i = 0;i<s1.length();i++) {
			char a = s1.charAt(i);
			int pos = Arrays.binarySearch(s2_char, a);
			if(pos<0) {
				System.out.println(pos);
				return false;
			}
		}
		return true;

	}

	public static void main(String[] args) {
		String s1 = "asdfq";
		String s2 = "dafse";
		System.out.println(check(s1, s2));
	}

}

7. 数组能排成的最小数

问题描述:

给定一个int数组arr,将int数组arr中的数组合成最小的整数

思路解析:

将int数组arr按照字符的字典序进行排序,这样能保证最前面的数字段序是最小的,然后从前到后进行字符拼接,此时拼接出来的数即位最小的数

package 排序;

import java.util.Arrays;
import java.util.Comparator;

/**
 * @author: DreamCode
 * @file: 数组能排成的最小数.java
 * @time: 2022年3月7日-下午9:07:06
 */
public class 数组能排成的最小数 {

	public static void main(String[] args) {
		Integer[] arr = { 3, 32, 321 };
		int res = f(arr);
		System.out.println(res);

	}

	private static int f(Integer[] arr) {
		// TODO 特殊排序
		Arrays.sort(arr, new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
				// TODO o1对于o2:小于、等于、大于分别返回-1,0,1
				String s1 = o1 + "" + o2;
				String s2 = o2 + "" + o1;
				return s1.compareTo(s2);
			}

		});
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < arr.length; i++) {
			stringBuilder.append(arr[i]);
		}
		return Integer.parseInt(stringBuilder.toString());
	}

}

8. 需排序的子数组长度

问题描述:

有一个整数数组,存在索引a和b,只要将a和b之间的元素排好序,整个数组就是有序(升序)的。 注意:b-a应该越小越好,也就是说,找出符合条件的最短序列。

给定一个int数组A和数组的大小n,请输出a和b,若原数组有序,输出0和0。保证A中元素均为正整数。

测试样例: 6 1 4 6 5 9 10 输出:2 3

思路解析:

分别记录两边需要排序元素的边界即可。具体来说就是:

从左往右遍历,记录左侧出现的最大值,记为 max ,如果当前数字 a[i] < max,说明若排序,max 必然会挪到 a[i] 右边,记录最右边出现这种情况的位置;

从右往左遍历,记录右侧出现的最小值,记为 min ,如果当前数字 a[i] > min, 说明若排序,min 必然会挪到 a[i] 左边,记录最左边出现这种情况的位置。

这样得到的两个坐标之间的数字就是需要排序的子数组。

package 排序;

/**
 * @author: DreamCode
 * @file: 需排序的子数组长度.java
 * @time: 2022年3月7日-上午10:55:14
 */
public class 需排序的子数组长度 {

	public static void main(String[] args) {
		int[] arr = {2,9,4,6,5,9,10};
		int[] res = findsigment(arr);
		System.out.println(res[0]+" "+res[1]);

	}

	private static int[] findsigment(int[] arr) {
		int n = arr.length;
		int min = Integer.MAX_VALUE,max=Integer.MIN_VALUE,p1=-1,p2=-1;
		for(int i=0;i<n;i++) {  
			if(arr[i]>max) {  //更新最大值
				max=arr[i];
			}
			if(arr[i]<max) {  //当前值比最大值小,说明排序后最大值必然会在该值之后
				p2=i;
			}
		}
		for(int i=n-1;i>=0;i--) {
			if(arr[i]<min) {  //更新最小值
				min=arr[i];
			}
			if(arr[i]>min) {  //当前值比最小值大,说明排序后最小值必然会在该值的之前
				p1=i;
			}
		}
		if(p1==-1) {
			return new int[] {0,0};
		}
		return new int[]{p1,p2};
	}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

梦码城

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

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

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

打赏作者

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

抵扣说明:

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

余额充值