👉👀💬今日练习(一)快速排序(Quick Sort)
。 🙇思路
快速排序是一种既节省空间又比比较快速的一个排序算法。当然这个算法是不稳定的,极端情况下退化为冒泡的O(n^2)的时间复杂度。接下来我们来看快排的思路:
首先我们在给定的序列中随机找一个基准数,别被这个基准数吓到,这个数仅仅是用来做一个参考数,快排的思路就是将小于基准数的值放到基准数的左边,将大于基准数的值放到序列的右边,举例:假设给定的序列是6 1 2 7 9 3 4 5 10 8这十个数,设基准数为6,按照快排的思路,我们需要将这个序列变成如下的序列:3 1 2 5 4 6 9 7 10 8,仔细看可以看出,6的位置刚好是排序完成后的正确位置,我们称之为归位。那如何变成这样的序列呢?
我们从序列的两端开始查找,先从最右端(j)开始找,找一个数使得小于基准数6,然后从左端(i)开始查找,找一个数使得大于基准数,按照上面给定的序列,我们第一次找到的两个数分别是5和7,交换这两个数得到序列:6 1 2 5 9 3 4 7 10 8,继续寻找,然后分别找到两个数4和9,交换这两个数得到序列:6 1 2 5 4 3 9 7 10 8,继续寻找,这时候i和j相遇,这个时候结束查找,将i、j相遇的位置和基准数交换得到序列::3 1 2 5 4 6 9 7 10 8,这也就完成了我们快排最核心的一步,将小于基准数的值放到基准数的左边,将大于基准数的值放到序列的右边,之后的操作就简单了,递归的将基准数两边的序列按照快排的思路依次找基准数进行基准数的归位。
代码:
public void quickSort(int[] arr, int low, int high) { int i,j,temp,t; if(low>high){ return; } i=low; j=high; //temp就是基准位 temp = arr[low]; while (i //先看右边,依次往左递减,找一个数是的小于基准值 while (temp<=arr[j]&&i j--; } //再看左边,依次往右递增,找一个数使得大于基准值。 while (temp>=arr[i]&&i i++; } //如果满足条件,则交换 if (i t = arr[j]; arr[j] = arr[i]; arr[i] = t; } } //最后将基准为与i和j相等位置的数字交换 arr[low] = arr[i]; arr[i] = temp; //递归调用左半数组 quickSort(arr, low, j-1); //递归调用右半数组 quickSort(arr, j+1, high);}
复杂度分析
- 时间复杂度:T(n) = O(nlogn)。
- 空间复杂度:T(n) = O(n2)。
- 平均情况:T(n) = O(nlogn)。
👉👀💬今日练习(二)堆排序(Heap Sort)。
🙋介绍两个概念:大顶堆与小顶堆
- 大顶堆原理:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大顶堆。大顶堆要求根节点的关键字既大于或等于左子树的关键字值,又大于或等于右子树的关键字值。
- 小顶堆原理:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者,称为小顶堆。小堆堆要求根节点的关键字既小于或等于左子树的关键字值,又小于或等于右子树的关键字值。
🙇思路
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
- 构建初始堆,将待排序列构成一个大顶堆(或者小顶堆),升序大顶堆,降序小顶堆;
- 将堆顶元素与堆尾元素交换,并断开(从待排序列中移除)堆尾元素。
- 重新构建堆。
- 重复2~3,直到待排序列中只剩下一个元素(堆顶元素)。
/*** 创建堆,* @paramarr 待排序列*/public void heapSort(int[] arr) { //创建堆 for (int i = (arr.length - 1) / 2; i >= 0; i--) { //从第一个非叶子结点从下至上,从右至左调整结构 adjustHeap(arr, i, arr.length); } //调整堆结构+交换堆顶元素与末尾元素 for (int i = arr.length - 1; i > 0; i--) { //将堆顶元素与末尾元素进行交换 int temp = arr[i]; arr[i] = arr[0]; arr[0] = temp; //重新对堆进行调整 adjustHeap(arr, 0, i); }}/*** 调整堆* @paramarr 待排序列* @paramparent 父节点* @paramlength 待排序列尾元素索引*/public void adjustHeap(int[] arr, int parent, int length) { //将temp作为父节点 int temp = arr[parent]; //左孩子 int lChild = 2 * parent + 1; while (lChild < length) { //右孩子 int rChild = lChild + 1; // 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点 if (rChild < length && arr[lChild] < arr[rChild]) { lChild++; } // 如果父结点的值已经大于孩子结点的值,则直接结束 if (temp >= arr[lChild]) { break; } // 把孩子结点的值赋给父结点 arr[parent] = arr[lChild]; //选取孩子结点的左孩子结点,继续向下筛选 parent = lChild; lChild = 2 * lChild + 1; } arr[parent] = temp;}
复杂度分析
- 时间复杂度:T(n) = O(nlogn)。
- 空间复杂度:T(n) = O(nlogn)。
- 平均情况:T(n) = O(nlogn)。
不积跬步,无以至千里。