常见排序及其Java实现


import java.util.Random;
/*
1冒泡排序
2快速排序
3归并排序 递归
4归并排序 迭代
5堆排序
 */
public class sortDemo1 {
    public static void main(String[] args) {
        int[] num1 = new int[]{1,6,7,5,3,2,9,4,8};
        int[] num2 = new int[]{1,6,7,5,3,2,9,4,8};
        int[] num3 = new int[]{1,6,7,5,3,2,9,4,8};
        int[] num4 = new int[]{1,6,7,5,3,2,9,4,8};
        int[] num5 = new int[]{1,6,7,5,3,2,9,4,8};
        final int length = 9;

        MySort bubbleSort = new BubbleSort();
        bubbleSort.sort(num1);
        System.out.println("冒泡排序:");
        for (int i = 0; i < length; i++) {
            System.out.print(num1[i] + " ");
        }
        System.out.println();

        MySort quickSort = new QuickSort();
        quickSort.sort(num2);
        System.out.println("快速排序:");
        for (int i = 0; i < length; i++) {
            System.out.print(num2[i] + " ");
        }
        System.out.println();

        MySort mergeSort = new MergeSortDFS();
        mergeSort.sort(num3);
        System.out.println("归并排序 递归:");
        for (int i = 0; i < length; i++) {
            System.out.print(num3[i] + " ");
        }
        System.out.println();

        MySort mergeSortNotDFS = new MergeSortNotDFS();
        mergeSortNotDFS.sort(num4);
        System.out.println("归并排序 迭代:");
        for (int i = 0; i < length; i++) {
            System.out.print(num4[i] + " ");
        }
        System.out.println();

        MySort heapSort = new HeapSort();
        heapSort.sort(num5);
        System.out.println("堆排序:");
        for (int i = 0; i < length; i++) {
            System.out.print(num5[i] + " ");
        }
        System.out.println();
    }
}

interface MySort {
    void sort(int[] nums);
}

/*
冒泡排序 升序 时间复杂度 O(n^2)
相邻元素比较,升序则不变,否则交换。
第一(i=0)次操作从索引0和1开始比较直到n-2和n-1,结束后确定最后一个元素是最大。
每次都确定最后一个元素是最大,所以每次循环的范围都减少i
 */
class BubbleSort implements MySort {
    @Override
    public void sort(int[] nums) {
        int n = nums.length;
        for(int i = 0; i < n - 1; i++) {
            //判断是否顺序已经正确,可以提前终止循环
            boolean flag = true;

            for(int j = 0; j < n - 1 - i; j++) {
                if(nums[j] > nums[j + 1]) {
                    int tmp = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = tmp;

                    flag = false;
                }
            }

            //如果对一个i 这一次循环中都没有发生交换行为,就说明顺序已经确定了
            if(flag) break;
        }
    }
}


/*
快速排序
期望时间复杂度是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。
快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。
所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。

算法步骤:
从数列中挑出一个元素,称为 "基准"(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
 */
class QuickSort implements MySort {
    @Override
    public void sort(int[] nums) {
        quickSort(nums, 0, nums.length - 1);
    }
    private void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
    private void quickSort(int[] nums, int l, int r) {
        if(l >= r) return;
        int randomInt = new Random().nextInt(l, r + 1);//[l,r+1)的随机整数

        int i = l - 1, j = r + 1, pivot = nums[randomInt];
        while (i < j) {
            do i++; while (nums[i] < pivot);
            do j--; while (nums[j] > pivot);//这里有可能导致j多减一步,所以下面注意j的范围
            if(i < j) {
                swap(nums, i, j);
            }
        }
        //i j 的范围一定在l和r之间
        quickSort(nums, l, j);//[l,j] 无论是j多减一步 和 没多减 都是在标志位左边
        quickSort(nums, j + 1, r);//[j+1,r] 无论是j多减一步 和 没多减 都是在标志位右边
    }
}


/*
归并排序 时间O(nlogn) 空间O(n)
作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:
自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);
自下而上的迭代;

申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
重复步骤 3 直到某一指针达到序列尾;
将另一序列剩下的所有元素直接复制到合并序列尾。
 */
class MergeSortDFS implements MySort {
    int[] copy;//提前开辟数组,避免之后频繁创建的消耗
    @Override
    public void sort(int[] nums) {
        copy = new int[nums.length];
        mergeSortDFS(nums, 0, nums.length - 1);
    }
    private void mergeSortDFS(int[] nums, int start, int end) {
        if(start >= end) return;//下面只有可能start < end
        int mid = (start + end) >> 1;//由于[start,end]至少有两个元素,所以mid是向下取整
        mergeSortDFS(nums, start, mid);
        mergeSortDFS(nums, mid + 1, end);

        merge(nums, start, end, mid);
    }

    private void merge(int[] nums, int start, int end, int mid) {
        int tmp1 = start, tmp2 = mid + 1, i = start;
        while (tmp1 <= mid && tmp2 <= end) {
            if(nums[tmp1] < nums[tmp2]) {
                copy[i++] = nums[tmp1++];
            }
            else copy[i++] = nums[tmp2++];
        }
        while (tmp1 <= mid) {
            copy[i++] = nums[tmp1++];
        }
        while (tmp2 <= end) {
            copy[i++] = nums[tmp2++];
        }
        //对合并的部分进行赋值
        for (int j = start; j <= end; j++) {
            nums[j] = copy[j];
        }
    }
}

class MergeSortNotDFS implements MySort {
    int[] copy;//提前开辟数组,避免之后频繁创建的消耗
    @Override
    public void sort(int[] nums) {
        copy = new int[nums.length];
        mergeSortNotDFS(nums);
    }
    private void mergeSortNotDFS(int[] nums) {
        int n = nums.length;
        for(int subLength = 1; subLength < n; subLength <<= 1) {//迭代的写法 从长度为1开始
            //每次合并两个子数组时的起始位置start 前一个数组的结束位置mid 后一个数组的结束位置end
            int start = 0, mid = start + subLength - 1, end = start + 2 * subLength - 1;
            while (mid <= n - 1) {//单个子数组长度小于总长度时,进行合并
                merge(nums, start, Math.min(end, n - 1), mid);
                start += 2 * subLength;
                mid += 2 * subLength;
                end += 2 * subLength;
            }
        }
    }

    private void merge(int[] nums, int start, int end, int mid) {
        int tmp1 = start, tmp2 = mid + 1, i = start;
        while (tmp1 <= mid && tmp2 <= end) {
            if(nums[tmp1] < nums[tmp2]) {
                copy[i++] = nums[tmp1++];
            }
            else copy[i++] = nums[tmp2++];
        }
        while (tmp1 <= mid) {
            copy[i++] = nums[tmp1++];
        }
        while (tmp2 <= end) {
            copy[i++] = nums[tmp2++];
        }
        //对合并的部分进行赋值
        for (int j = start; j <= end; j++) {
            nums[j] = copy[j];
        }
    }

}

/*
堆排序(Heapsort) 平均时间复杂度为 Ο(nlogn)。
是指利用堆这种数据结构所设计的一种排序算法。
堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。

分为两种方法:
大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;(根节点最大,每次根节点和尾结点交换,最大值在尾结点,升序
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;(根节点最小,每次根节点和尾结点交换,最小值在尾结点,降序


算法步骤:
创建一个堆 H[0……n-1]
把堆首(最大值)和堆尾互换 把堆的尺寸缩小 并维护堆的性质(从下向上遍历!!因为必须要上浮最大值。从上向下维护!!因为要将较小值下沉。)
重复直到全排序完成
 */
class HeapSort implements MySort {

    @Override
    public void sort(int[] nums) {
        heapSort(nums);
    }

    private void heapSort(int[] nums) {
        int n = nums.length;
        //建堆
        buildHeap(nums);
        //每次交换堆首和堆尾
        for (int i = n - 1; i >= 1; i--) {
            swap(nums, 0, i);//交换堆首和堆尾
            heapify(nums, 0, i);//当前的堆大小为i
        }
    }
    private void swap(int[] nums, int i, int j) {//交换数组元素的值
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
    private void heapify(int[] nums, int k, int n) {//维护大根堆的性质 在下标k处 n表示堆的大小
        int leftson = 2 * k + 1, rightson = 2 * k + 2;
        int large = k;
        if(leftson < n && nums[large] < nums[leftson]) large = leftson;
        if(rightson < n && nums[large] < nums[rightson]) large = rightson;
        //上述可以用large找到k leftson rightson三者中最大的数的下标

        if(large != k) {//说明根节点非最大,进行交换
            swap(nums, large, k);
            heapify(nums, large, n);//递归的向下维护大根堆
        }
    }
    private void buildHeap(int[] nums) {//建堆(大根堆
        int n = nums.length;
        for(int i = (n - 2) / 2; i >= 0; i--) {
            heapify(nums, i, n);
        }
    }
}

力扣912

//堆排序
class Solution {
    public int[] sortArray(int[] nums) {
        int n = nums.length;
        buildHeap(nums);
        for(int i = n - 1; i >= 1; i--) {
            swap(nums, i, 0);
            heapify(nums, 0, i);
        }
        return nums;
    }
    private void heapify(int[] nums, int k, int n) {//k为要处理的下标 n为堆的大小
        int leftson = 2 * k + 1, rightson = 2 * k + 2;
        int large = k;
        if(leftson < n && nums[large] < nums[leftson]) large = leftson;
        if(rightson < n && nums[large] < nums[rightson]) large = rightson;
        if(large != k) {
            swap(nums, large, k);
            heapify(nums, large, n);
        }
    }
    private void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
    private void buildHeap(int[] nums) {
        int n = nums.length;
        for(int i = (n - 2) / 2; i >= 0; i--) {
            heapify(nums, i, n);
        }
    }
}

// //归并排序的迭代写法 
// class Solution {
//     int[] copy;
//     public int[] sortArray(int[] nums) {
//         copy = new int[nums.length];
//         return mergeSortNotDFS(nums);
//     }

//     private int[] mergeSortNotDFS(int[] nums) {
//         int n = nums.length;
//         for(int subLength = 1; subLength < n; subLength <<= 1) {//迭代的写法 从长度为1开始
//             //每次合并两个子数组时的起始位置start 前一个数组的结束位置mid 后一个数组的结束位置end
//             int start = 0, mid = start + subLength - 1, end = start + 2 * subLength - 1;
//             while (mid <= n - 1) {//单个子数组长度小于总长度时,进行合并
//                 merge(nums, start, Math.min(end, n - 1), mid);
//                 start += 2 * subLength;
//                 mid += 2 * subLength;
//                 end += 2 * subLength;
//             }
//         }
//         return nums;
//     }

//     private void merge(int[] nums, int start, int end, int mid) {
//         int tmp1 = start, tmp2 = mid + 1, i = start;
//         while (tmp1 <= mid && tmp2 <= end) {
//             if(nums[tmp1] < nums[tmp2]) {
//                 copy[i++] = nums[tmp1++];
//             }
//             else copy[i++] = nums[tmp2++];
//         }
//         while (tmp1 <= mid) {
//             copy[i++] = nums[tmp1++];
//         }
//         while (tmp2 <= end) {
//             copy[i++] = nums[tmp2++];
//         }
//         //对合并的部分进行赋值
//         for (int j = start; j <= end; j++) {
//             nums[j] = copy[j];
//         }
//     }
// }



// //归并排序的递归写法 注意 要把copy的数组提前开辟
// class Solution {
//     int[] copy;
//     public int[] sortArray(int[] nums) {
//         copy = new int[nums.length];
//         return mergeSortDFS(nums, 0, nums.length - 1);
//     }
//     private int[] mergeSortDFS(int[] nums, int start, int end) {
//         if(start >= end) return nums;//下面只有可能start < end
//         int mid = (start + end) >> 1;//由于[start,end]至少有两个元素,所以mid是向下取整
//         mergeSortDFS(nums, start, mid);
//         mergeSortDFS(nums, mid + 1, end);

//         merge(nums, start, end, mid);
//         return nums;
//     }

//     private void merge(int[] nums, int start, int end, int mid) {
//         int tmp1 = start, tmp2 = mid + 1, i = start;
//         while (tmp1 <= mid && tmp2 <= end) {
//             if(nums[tmp1] < nums[tmp2]) {
//                 copy[i++] = nums[tmp1++];
//             }
//             else copy[i++] = nums[tmp2++];
//         }
//         while (tmp1 <= mid) {
//             copy[i++] = nums[tmp1++];
//         }
//         while (tmp2 <= end) {
//             copy[i++] = nums[tmp2++];
//         }
//         //对合并的部分进行赋值
//         for (int j = start; j <= end; j++) {
//             nums[j] = copy[j];
//         }
//     }
// }

//冒泡排序 超时
// class Solution {
//     public int[] sortArray(int[] nums) {
//         int n = nums.length;
//         for(int i = 0; i < n - 1; i++) {
//             //判断是否顺序已经正确,可以提前终止循环
//             boolean flag = true;

//             for(int j = 0; j < n - 1 - i; j++) {
//                 if(nums[j] > nums[j + 1]) {
//                     int tmp = nums[j];
//                     nums[j] = nums[j + 1];
//                     nums[j + 1] = tmp;

//                     flag = false;
//                 }
//             }

//             //如果对一个i 这一次循环中都没有发生交换行为,就说明顺序已经确定了
//             if(flag) break;
//         }
//         return nums;
//     }
// }

//快速排序
// class Solution {
//     public int[] sortArray(int[] nums) {
//         return quickSort(nums, 0, nums.length - 1);
//     }
//     private int[] quickSort(int[] nums, int l, int r) {
//         if(l >= r) return nums;
//         int randomInt = new Random().nextInt(l, r + 1);
//         int i = l - 1, j = r + 1, pivot = nums[randomInt];
//         while (i < j) {
//             do i++; while (nums[i] < pivot);
//             do j--; while (nums[j] > pivot);//这里有可能导致j多减一步,所以下面注意j的范围
//             if(i < j) {
//                 swap(nums, i, j);
//             }
//         }
//         //i j 的范围一定在l和r之间
//         quickSort(nums, l, j);//[l,j] 无论是j多减一步 和 没多减 都是在标志位左边
//         quickSort(nums, j + 1, r);//[j+1,r] 无论是j多减一步 和 没多减 都是在标志位右边
//         return nums;
//     }
//     private void swap(int[] nums, int i, int j) {
//         int tmp = nums[i];
//         nums[i] = nums[j];
//         nums[j] = tmp;
//     }
// }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值