排序
1.从未排序序列中依次取出元素与已排序序列中的元素进行比较,将其放入已排序序列的正确位置上的方法,这种排序方法称为( 插入排序)。
插入排序基本思想:每一步将一个待排序的数据插入到前面已经排好序的有序序列中,直到插完所有元素为止。
插入排序的代码实现:
void StraightSort(int *arr,int len){ int tmp; // 用于暂存当前元素的值 int i; // 外层循环计数器 int j; // 内层循环计数器 for (i = 1; i < len; i++){ // 遍历数组,从第二个元素开始 tmp = arr[i]; // 将当前元素的值暂存到tmp变量中 for (j = i - 1; j >= 0 && arr[j] > tmp; j--){ // 内层循环用于将当前元素插入到已排序的序列中的正确位置 arr[j + 1] = arr[j]; // 如果前一个元素比当前元素大,则将前一个元素后移一位 } arr[j + 1] = tmp; // 将当前元素插入到正确位置 } }
希尔排序(shell排序)基本思想:希尔排序是把序列按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量的逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个序列恰好被分为一组,算法便终止。
希尔排序需要定义一个增量,这里选择增量为gap=length/2,缩小增量以gap=gap/2的方式,这个增量可以用一个序列来表示,{n/2,(n/2)/2....1},称为增量序列,这个增量是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。
希尔排序代码如下:
// Shell排序算法,对数组arr进行排序,数组长度为len void ShellSort(int *arr, int len) { // 使用希尔增量进行分组 for (int gap = len/2; gap > 0; gap = gap/2) { // 对每个分组进行插入排序 for (int i = gap; i < len; i++) { int j = i; // 插入排序 while (j - gap >= 0 && arr[j] < arr[j - gap]) { Swap(arr, j, j - gap); // 调用Swap函数交换元素 j = j - gap; } } } }
归并排序基本思想:归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
void merge_sort(int q[], int l, int r) { //递归的终止情况 if(l >= r) return; //第一步:分成子问题 int mid = l + r >> 1; //第二步:递归处理子问题 merge_sort(q, l, mid ), merge_sort(q, mid + 1, r); //第三步:合并子问题 int k = 0, i = l, j = mid + 1, tmp[r - l + 1]; while(i <= mid && j <= r) if(q[i] <= q[j]) tmp[k++] = q[i++]; else tmp[k++] = q[j++]; while(i <= mid) tmp[k++] = q[i++]; while(j <= r) tmp[k++] = q[j++]; for(k = 0, i = l; i <= r; k++, i++) q[i] = tmp[k]; }
冒泡排序的基本思想就是:从无序序列头部开始,进行两两比较,根据大小交换位置,直到最后将最大(小)的数据元素交换到了无序队列的队尾,从而成为有序序列的一部分;下一次继续这个过程,直到所有数据元素都排好序。
算法的核心在于每次通过两两比较交换位置,选出剩余无序序列里最大(小)的数据元素放到队尾。
冒泡排序的代码:
void bubble_sort(int arr[], int len) { int i, j; for (i = 0; i < len - 1; i++) for (j = 0; j < len - 1 - i; j++) if (arr[j] > arr[j + 1]) swap(arr[j], arr[j + 1]); }
选择排序的思想:利用线性查找搜索出待排序列中的最小(或最大)元素,并将它移动到最前面,每完成一次遍历,都会使一个元素在正确位置,即第i趟排序后,前面i个元素在正确位置。
选择排序的代码:
// 选择排序 // 每次找到一个最小的 放在正确的位置 public void selectsort(int[] a) { int k = 0; int temp = 0; for (int i = 0; i < a.length - 1; i++) {//i控制每趟循环,从第一个元素开始 k = i;//第i趟,k取出第i个数据与后边的数据进行比较(假设k此时为最小元素) for (int j = i; j < a.length; j++) {//内层循环用于找出最小元素 if (a[j] < a[k]) { k = j;//k用来记录最小元素的下标 } } temp = a[i]; a[i] = a[k]; a[k] = temp;//将最小数据与待排序列的第一个元素交换 } for (int num : a) { System.out.print(num + " "); } }
快速排序属于分治算法,分治算法都有三步:分成子问题 递归处理子问题 子问题合并
快速排序的代码:
void quick_sort(int q[], int l, int r) { //递归的终止情况 if(l >= r) return; //第一步:分成子问题 int i = l - 1, j = r + 1, x = q[l + r >> 1]; while(i < j) { do i++; while(q[i] < x); do j--; while(q[j] > x); if(i < j) swap(q[i], q[j]); } //第二步:递归处理子问题 quick_sort(q, l, j), quick_sort(q, j + 1, r); //第三步:子问题合并.快排这一步不需要操作,但归并排序的核心在这一步骤 }
2.从未排序序列中挑选元素,并将其依次放入已排序序列(初始时为空)的一端的方法,称为(选择排序)。
3.对n个不同的关键字由小到大进行冒泡排序,在下列( 从大到小排列好的)情况下比较的次数最多。
4.快速排序在下列( 被排序的数据完全无序)情况下最易发挥其长处。
被排序的数据中含有多个相同排序码
被排序的数据已基本有序
被排序的数据完全无序
被排序的数据中的最大值和最小值相差悬殊
5.对n个不同的排序码进行冒泡排序,在元素无序的情况下比较的次数最多为(n(n-1)/2)。
6.对n个关键字作快速排序,在最坏情况下,算法的时间复杂度是(O(n2))。
7.
若一组记录的排序码为(46, 79,56,38,40,84),则利用快速排序的方法,以第一个记录为基准得到的一次划分结果为( 40,38,46,56,79,84)。
先从中间分割
双指针是一个办法先i指向46,j指向84;
i指向46,j指向40所以调换;
i指向79,j指向46所以调换;
i指向46,j指向38所以调换;
i指向56,j指向46所以调换;
结果如上。
8.
下列关键字序列中,( d)是堆。
16,72,31,23,94,53
94,23,31,72,16,53
16,53,23,94,31,72
16,23,53,31,94,72
小顶堆,将所有数据序列按完全二叉树从根开始放,如果所有分支都小于或者等于孩子结点关键码,就是小顶堆,反之,如果所有分支结点的关键码大于或者等于孩子结点关键码,则为大顶堆
9.堆是一种( 选择)排序。
选择排序分为直接选择排序和堆排序。
堆排序的基本思想是:1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。
堆排序代码:
public class HeapSort { public static void heapSort(int[] arr) { if (arr == null || arr.length == 0) { return; } int len = arr.length; // 构建大顶堆,这里其实就是把待排序序列,变成一个大顶堆结构的数组 buildMaxHeap(arr, len); // 交换堆顶和当前末尾的节点,重置大顶堆 for (int i = len - 1; i > 0; i--) { swap(arr, 0, i); len--; heapify(arr, 0, len); } } private static void buildMaxHeap(int[] arr, int len) { // 从最后一个非叶节点开始向前遍历,调整节点性质,使之成为大顶堆 for (int i = (int)Math.floor(len / 2) - 1; i >= 0; i--) { heapify(arr, i, len); } } private static void heapify(int[] arr, int i, int len) { // 先根据堆性质,找出它左右节点的索引 int left = 2 * i + 1; int right = 2 * i + 2; // 默认当前节点(父节点)是最大值。 int largestIndex = i; if (left < len && arr[left] > arr[largestIndex]) { // 如果有左节点,并且左节点的值更大,更新最大值的索引 largestIndex = left; } if (right < len && arr[right] > arr[largestIndex]) { // 如果有右节点,并且右节点的值更大,更新最大值的索引 largestIndex = right; } if (largestIndex != i) { // 如果最大值不是当前非叶子节点的值,那么就把当前节点和最大值的子节点值互换 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; } }
10.堆的形状是一棵(完全二叉树 )。
11.若一组记录的排序码为(46,79,56,38,40,84),则利用堆排序的方法建立的初始堆为( 84,79,56,38,40,46)。
12.下述几种排序方法中,要求内存最大的是(c )。
希尔排序
快速排序
归并排序
堆排序
排序、希尔排序的空间复杂度为O(1),快速排序的空间复杂度为O(log2n),归并排序的空间复杂度为O(n)。
13.
下述几种排序方法中,( c)是稳定的排序方法。
希尔排序
快速排序
归并排序
堆排序
14.
数据表中有10000个元素,如果仅要求求出其中最大的10个元素,则采用( d)算法最节省时间。
冒泡排序
快速排序
简单选择排序
堆排序
堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。
15.)下列排序算法中,( a)不能保证每趟排序至少能将一个元素放到其最终的位置上。
希尔排序
快速排序
冒泡排序
堆排序
快速排序的每趟排序能将作为枢轴的元素放到最终位置;
冒泡排序的每趟排序能将最大或最小的元素放到最终位置;
堆排序的每趟排序能将最大或最小的元素放到最终位置。