排序算法
选择排序
public static void main(String[] args) {
int[] arr = {4, 5, 76, 3, 73, 67546, 734, 56, 4, 324, 36, 3, 44, 234, 36, 54, 6, 242, 35, 3};
//选择排序
//从小到大
int len = arr.length;
int k = 0;
int temp = 0;
for (int i = 0; i < len - 1; i++) {
k = i;
for (int j = i + 1; j < len; j++) {
//每次找最小 假如k第几小数的下标
if (arr[j] < arr[k]) {
//加入arr i 是最小的 如果有比arr i还要小的 先记录 为 k
k = j;
}
}
if (k == i) {
//不用发生交换
continue;
} else {
//找第小 需要交换
temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
System.out.println("after sorted " + Arrays.toString(arr));
}
插入排序
public static void main(String[] args) {
int[] arr = {14, 5, 716, 3, 73, 67546, 734, 56, 44, 324, 36, 38, 44, 234, 36, 54, 6, 242, 35, 3};
//4
//4 5
//4 5 76
//3 4 5 76
//插入排序
//从小到大
int length = arr.length;
for (int i = 1; i < length; i++) {
int j = i - 1;
/*while (arr[i] < arr[j]) {
//后面数大于前面的数 尽可能的找小于该数的下标 下标 j 前移 直到移到-1
j--;
if (j == -1) {
break;
}
}*/
while (j != -1 && arr[i] < arr[j]) {
//后面数大于前面的数 尽可能的找小于该数的下标 下标 j 前移 直到移到-1
j--;
}
if (j + 1 == i) {
//没有发生移动 break
continue;
} else {
//发送了移动 记录要插入的数为temp 找到的下标+1 (j+1) 往后移腾出位置 给temp
int temp = arr[i];
for (int k = i - 1; k >= j + 1; k--) {
arr[k + 1] = arr[k];
}
//找到位置后 插入temp
arr[j + 1] = temp;
}
}
System.out.println("after sorted " + Arrays.toString(arr));
}
交换排序(冒泡排序)
public static void main(String[] args) {
int[] arr = {4, 5, 76, 3, 73, 67546, 734, 56, 4, 324, 36, 3, 44, 234, 36, 54, 6, 242, 35, 3};
//冒泡排序
//从小到大
int length = arr.length;
int temp = 0;
for (int i = 0; i < length-1; i++) {
for (int j = 0; j < length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println("after sorted " + Arrays.toString(arr));
}
归并排序
public static void main(String[] args) {
int[] arr = {12, 5, 6, 82, 54, 62, 67, 82, 23, 54, 763, 52};
System.out.println("before sorted: "+Arrays.toString(arr));
mergeSort(arr, 0, arr.length - 1, new int[arr.length]);
System.out.println("after sorted: "+Arrays.toString(arr));
}
//拆分
/**
* @param arr 传入的要排序的数组
* @param left 起始下标
* @param right end下标
* @param temp 辅助数组
*/
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
//有两以上元素就还要再分
if (left < right) {
int mid = (left + right) / 2;
//左边再分
mergeSort(arr, left, mid, temp);
//右边再分
mergeSort(arr, mid + 1, right, temp);
//(有序的)合并
merge(arr, left, mid, right, temp);
}
}
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;
int j = mid + 1;
int t = 0;
while (i <= mid && j <= right) {
if (arr[i] < arr[j]) {
temp[t++] = arr[i++];
} else {
//(arr[i] >= arr[j]
temp[t++] = arr[j++];
}
}
while (i <= mid) {
temp[t++] = arr[i++];
}
while (j <= right) {
temp[t++] = arr[j++];
}
t = 0;
int tempLeft = left;
while (tempLeft <= right) {
arr[tempLeft++] = temp[t++];
}
}
快速排序
public static void main(String[] args) {
int[] arr = {145, 46, 67, 625, 3435, 436, 36, 546, 85, 65, 35, 323, 432, 56, 4476, 25, 76846, 34, 54, 725, 75436};
System.out.println("before sorted: " + Arrays.toString(arr));
quickSort(arr, 0, arr.length - 1);
System.out.println("after sorted: " + Arrays.toString(arr));
}
private static void quickSort(int[] arr, int left, int right) {
if (left > right) {
return;
}
//key为基准
int key = arr[left];
//i 和 j 为哨兵 arr[j]大于等于key j左移 j--
// arr[i]小于等于key 右移 i++
int i = left;
int j = right;
while (i < j) {
//注意: 必须要先充右到左 在重左到右
//j移动
while (arr[j] >= key && i < j) {
j--;
}
//i移动
while (arr[i] <= key && i < j) {
i++;
}
if (i < j) {
//左边大于等于key的 和 右边小于等于key的交换位置
//构成 左边的都是小于等于key的 右边的都是大于等于key的
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
//出了循环 说明 已经构成了
//构成 左边的都是小于等于key的 右边的都是大于等于key的
//基准的下标和 i和j相等时的下标交换位置
arr[left] = arr[i];
arr[i] = key;
System.out.println("sorting: " + Arrays.toString(arr));
//这时候基准是真的已经确定了 用基准左右两边的值进行递归找基准
quickSort(arr, left, i - 1);
quickSort(arr, i + 1, right);
}
希尔排序(插入排序改进)
//希尔排序 改善的插入排序
//参考插入排序 把-1 该为 -gap 外面加一层循环 for (; gap > 0; gap = gap / 2) 即可
public static void main(String[] args) {
int[] arr = {145, 46, 67, 625, 3435, 436, 36, 546, 85, 65, 35, 323, 432, 56, 4476, 25, 76846, 34, 54, 725, 75436};
//步长 按/2来循环 步长到1就和正常插入排序一样了
//前面的大于1的步长插入尽力的把数组往有序的靠近
System.out.println("before sorted: " + Arrays.toString(arr));
int gap = arr.length / 2;
int len = arr.length;
for (; gap > 0; gap = gap / 2) {
for (int i = gap; i < len; i+=gap) {
int j = i - gap;
while (j >= 0 - gap) {
if (arr[i] < arr[j]) {
j -= gap;
if (j != (0 - gap)) {
continue;
}
}
if (j + gap == i) {
//直接是后一位 什么都不用移动
break;
} else {
//要移动
int temp = arr[i];
for (int k = i - gap; k >= j + gap; k -= gap) {
//按步长 一个个 往后移动
arr[k + gap] = arr[k];
}
arr[j + gap] = temp;
break;
}
}
}
System.out.println("when gap: " + gap + Arrays.toString(arr));
}
System.out.println("after sorted: " + Arrays.toString(arr));
}
堆排序
public static void main(String[] args) {
//堆排序
int[] arr = {13, 12, 54, 76, 57, 5, 34, 3, 5, 76, 8, 8, 79, 8, 22, 645, 6, 57, 68, 5, 35, 47, 58, 6, 52};
System.out.println("before sorted: "+ Arrays.toString(arr));
if (arr == null || arr.length == 0) {
return;
}
//开始构建大顶堆
int len = arr.length;
buildMaxHeap(arr, len);
System.out.println("sorting: "+Arrays.toString(arr));
//开始交换 把树的根节点和数组的最后一个元素进行交换
//这种交换要发生len -1次
for (int i = len - 1; i > 0; i--) {
swap(arr, 0, i);
// 交换之后 大顶堆不存在 重新构建大顶堆 由于只是少了数的根节点
// 树本身原来就是大顶堆 所有对 根节点下标为0的子树 大顶堆化
// 如果发生交换 其他子树大叔大顶堆 由heapify方法自行进行控制 对要交换的元素递归调用heapify
// 把树的根节点移到数组最后(也就是len-1的位置) 把它当做数之外的元素 树的大小减一
len--;
heapify(arr, 0, len);
}
System.out.println("after sorted: "+ Arrays.toString(arr));
}
/**
* @param arr 数组
* @param len 所用数组中元素的个数
* 0
* 1 2
* 3 4 5 6
* 7 8 9
* <p>
* 0 1 2 3 4 5 6 7 8 9
*/
private static void buildMaxHeap(int[] arr, int len) {
//从最后一个非叶子节点开始向前遍历 调整节点性质 使其变为大顶堆
//公式计算该节点下标 i = (int) Math.floor(len /2) -1
int i = (int) Math.floor(len / 2) - 1;
for (; i >= 0; i--) {
heapify(arr, i, len);
}
}
/**
* 使根节点下标为i的子树变为大顶堆
* 大顶堆化
*
* @param arr 数组
* @param i 子树根节点下标
* @param len 可用数组中元素的个数 也就是整个数的大小
*/
private static void heapify(int[] arr, int i, int len) {
//堆的性质 计算左右节点对象的下标
int left = 2 * i + 1;
int right = 2 * i + 2;
//默认当前节点为最大值
int largestIndex = i;
//知道该父节点的左右孩子的下标 根据right < len 和 left < len
//判断左右节点是否真的存在 存在则比较
if (left < len && arr[left] > arr[largestIndex]) {
largestIndex = left;
}
if (right < len && arr[right] > arr[largestIndex]) {
largestIndex = right;
}
//找到最大 然后发送交换
if (i != largestIndex) {
// 如果不是自己才去交换
swap(arr, i, largestIndex);
//交换之后 子节点的值发送变化 为了保证还是个大顶堆 再次通过方法进行大顶堆化
heapify(arr, largestIndex, len);
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
基数排序
public static void main(String[] args) {
int[] arr = {145, 6, 5, 37, 344, 347, 59, 58, 300, 45,
3654, 25, 322, 4, 46, 57, 54, 3, 2, 354, 67, 65, 4, 345, 644, 7265, 7, 3, 5, 235, 67};
System.out.println("before sorted: "+ Arrays.toString(arr));
//基数排序 0--9 典型的空间换时间
//思路:构造一个二位数组 10行arr.length列 (为了防止数组的元素都是一个下标越界)
// 先拿各位进行排序 把各位的排序结果 按找二维数组 列优先的遍历方式 把不为空为元素放到arr数组里面 有的话依次十位 百位 千位
// 让构造的新数组参与下一轮排序 直到排到最高为 arr必然已经是一个有序的了 (一位前人总结的这个经验,仅限于正整数)
int[][] data = new int[10][arr.length]; //也可以用结合 更省内存
int[] digitElement = new int[10];
//要找最大的数 也就是要就算这个数组arr 的最大数有几位 最外层要进行几层循环才能得到完全有序
int max = -1;
for (int num : arr) {
if (max < num) {
max = num;
}
}
//得到位数 最外层循环次数
int bit = (max + "").length();
for (int i = 0; i < max; i++) {
int temp = (int) Math.pow(10, i);
for (int num : arr) {
int index = num / temp % 10;
data[index][digitElement[index]] = num;
digitElement[index]++;
}
//没一次外循环构建的是一个新的序列的数组
int j = 0;
for (int k = 0; k < digitElement.length; k++) {
if (digitElement[k] != 0) {
//不为0说明至少是有一个元素的 挨个赋值给arr 新的序列
for (int l = 0; l < digitElement[k]; l++) { //某位元素不止一个的都要都取到放arr里面去 不能少
arr[j++] = data[k][l];
}
}
//是通过 digitElement数组里面元素是否为0判断有无元素 有几个元素
//为了保证下次循环数组正确 使用完要置为0
digitElement[k] = 0;
}
}
System.out.println("after sorted: "+ Arrays.toString(arr));
}
计数排序
public static void main(String[] args) {
int[] arr = {13, 12, 54, 76, 57, 5, 34, 32, 45, 76, 84, 58, 79, 8, 22, 645, 6, 57, 68, 65, 35, 47, 58, 61, 52};
System.out.println("before sorted: "+ Arrays.toString(arr));
//计数排序 典型的空间还时间
//思想:原则上数组里面最大值为n 就构造一个n+1的数组 数组的下标天然有序 分别表示存储这些元素
//元素的下标就是表示该数 然后该元素的值就表示该元素的多少 然后遍历这各构造的函数 有值(值不为0)
//说明存在至少一个元素 构造的数组的下标就是这个元素 依次遍历构造的数组 挨个取值就是一个有序的数组了
// countSortOrigin(arr);
//优化:考虑构造的数组太大的原因这里可以取出数组的最大值和最小值 构造一个max-min+1的大小的数组 用于存储元素
int max = arr[0]; //找最大值
int min = arr[0]; //找最小值
for (int num : arr) {
if (max < num) {
max = num;
}
if (min > num) {
min = num;
}
}
int[] tempArr = new int[max-min+1]; //初始值每个都为0
for (int num : arr) {
tempArr[num-min]++; //这里一个巧妙的操作是num-min
}
int t = 0;
for (int i = 0; i < tempArr.length; i++) {
if (tempArr[i] != 0) {
for (int j = 0; j < tempArr[i]; j++) {
arr[t++] = i+min; //这里用到了那个巧妙的操作是i+min
}
}
}
System.out.println("after sorted: "+ Arrays.toString(arr));
}
private static void countSortOrigin(int[] arr) {
int max = arr[0]; //找最大值
for (int num : arr) {
if (max < num) {
max = num;
}
}
int[] tempArr = new int[max+1]; //初始值每个都为0
for (int num : arr) {
tempArr[num]++;
}
//把新数组挨个放到原数组里面
int t = 0;
for (int i = 0; i < tempArr.length; i++) {
if (tempArr[i] != 0) {
for (int j = 0; j < tempArr[i]; j++) {
arr[t++] = i;
}
}
}
System.out.println("after sorted: "+ Arrays.toString(arr));
}
桶排序
public static void main(String[] args) {
int[] arr = {13, 12, 54, 76, 57, 5, 34, 32, 45, 76, 84, 58, 79, 8, 22, 645, 6, 57, 68, 65, 35, 47, 58, 61, 52};
System.out.println("before sorted: " + Arrays.toString(arr));
//桶排序 空间换时间 把空间相对再缩小一点 用桶中不会有序的来补充
//某种意义上 计数排序就是特殊的桶排序 桶的数量达到了最大 并且每个桶里面都是排好序的
//思路:类别计数排序 每个桶挨个是有序的 桶内不一定有序 保证桶内元素有序 挨个取出挨个赋值到原数组 必然有序了
//总之 用计数排序来理解桶排序是很好理解的
int max = arr[0]; //找最大值
int min = arr[0]; //找最小值
for (int num : arr) {
if (max < num) {
max = num;
}
if (min > num) {
min = num;
}
}
//首先要确定桶的数量 计数排序的桶的数量就是max-min+1
//这里要空间相对再缩小一点 设置一个因子来缩小桶 factor
//默认的话使用数组的长度arr.length作为factor 也可以用其他的
int factor = arr.length;
int bucketCount = (max - min) / arr.length + 1; //这里直接就max - min 部跟计数排序那样谈优化了
//桶数量 加1t同样是防止数组下标越界
//拿计数排序举例 元素下标0开始最大元素为n 下标最大值为n-1 越界 计数排序优化同样是这个道理
//这里与计数排序不同的是 桶里面的元素不一样 只是一个区间里的 而且不一定有序
//所以至少需要一个二维的空间 可以选数组 可以选集合 前面的9个排序首先考虑都是选数组 这里选集合吧
ArrayList<ArrayList<Integer>> buckets = new ArrayList<>(bucketCount); //bucketCount 桶的大小
//防止空指针异常 桶中集合初始化
for (int i = 0; i < bucketCount; i++) {
buckets.add(new ArrayList<Integer>());
}
for (int num : arr) {
//获取小标 factor是那个因子 这里factor就是arr.length 因为这个factor不一定必须是length 两个分开来说
int index = (num - min) / factor;
buckets.get(index).add(num);
}
//然后就是桶内元素排序 随便选用一个排序算法 这里用jdk的api
for (ArrayList<Integer> bucket : buckets) {
Collections.sort(bucket);
}
//最后挨个取值赋值给原数组 排序就完成了
int t = 0;
for (ArrayList<Integer> bucket : buckets) {
int size = bucket.size();
if (size != 0) {
for (int i = 0; i < size; i++) {
arr[t++] = bucket.get(i);
}
}
}
System.out.println("after sorted: " + Arrays.toString(arr));
}
排序算法分析