排序算法笔记二

排序六:直接选择排序

直接选择排序也是一种简单的排序方法,它的基本思想是:第一次从data[0]~data[n-1]中选取最小值,与data[0]交换,第二次从data[1]~data[n-1]中选取最小值,与data[1]交换,....,第i次从data[i-1]~data[n-1]中选取最小值,与data[i-1]交换,.....,第n-1次从data[n-2]~data[n-1]中选取最小值,与data[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

int[] data= { 31, 23, 89, 10, 47, 68, 8, 22 };

【31 23 89 10 47 68 8 22 】data[0]=31与最小data[6]=8交换
【8 23 89 10 47 68 31 22 】data[1]=23与后面最小值data[3]=10交换
【8 10 89 23 47 68 31 22 】data[2]=89与后面最小值data[7]=22交换
【8 10 22 23 47 68 31 89 】data[3]=23小于后面的值,不用交换
【8 10 22 23 47 68 31 89 】data[4]=47与后面最小值data[6]=31交换
【8 10 22 23 31 68 47 89 】data[5]=68与后面最小值data[6]=47交换
【8 10 22 23 31 47 68 89 】data[6]=68小于后面的值,不用交换
排序完成

从小到大排序:

public class Sort {
    public static void main(String[] args) {
        int[] data = { 31, 23, 89, 10, 47, 68, 8, 22 };
        selectSort(data);
        showData(data);
    }

    // 打印数组
    private static void showData(int[] data) {
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + " ");
        }
    }

    // 直接选择排序
        private static void selectSort(int[] data) {
            int n = data.length;
            int i, j, k;
            int t;
            for (i = 0; i < n - 1; i++) {// 做第i趟排序(1≤i≤n-1)
                k = i;
                for (j = i + 1; j < n; j++) {
                    // 在当前无序区data[i..n]中选key最小的记录data[k]
                    if (data[j] < data[k]) {
                        k = j; // k记下目前找到的最小关键字所在的位置
                    }
                }
                if (k != i) { // 交换data[i]和data[k]
                    t = data[i];
                    data[i] = data[k];
                    data[k] = t; // t作暂存单元
                }
            }
        }
}

排序七:堆排序

1.堆排序是指利用堆这种数据结构所设计的一种选择排序。

堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]。

堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。

2.用大根堆排序的基本思想:

(1)用大根堆排序的基本思想

① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区

② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key

③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。

……

直到无序区只有一个元素为止。

(2)大根堆排序算法的基本操作:

① 初始化操作:将R[1..n]构造为初始堆;

② 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。

因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。

下面举例对31, 23, 89, 10, 47, 68, 8, 22进行堆排序。

首先根据该数组元素构建一个完全二叉树:

然后需要构造初始堆,则从最后一个非叶节点开始调整,调整过程如下:

10小于22,交换后

接着看上一个节点,89都比他的子节点大,不用交换

接着看上一个节点,23节点比它的子节点47小,交换后

接着看上一个节点,31都比它的子节点小,跟左右最大的节点交换,交换后31到89位置,又与它的子节点68小,再次交换,交换后

这样就得到了初始堆。

即每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。有了初始堆之后就可以进行排序了。

堆顶89与最后一个节点10交换,交换后10在堆顶位置,与它左右孩子节点小,与最大的右孩子节点68交换,交换后10到68位置又与它的左孩子节点31小,与31交换

交换后数组长度-1,接着堆顶68与最后一个数据即最后第二个节点8交换,8到堆顶位置与左右孩子节点小,8与最大的左孩子节点47交换,交换后8到47位置又与左右孩子节点小,与最大的右孩子节点23交换

交换后数组长度-1,接着堆顶47与最后一个数据即最后第三个节点10交换,10到堆顶与它左右孩子节点小,10与它最大的右孩子节点31交换

交换后数组长度-1,接着堆顶31与最后一个数据即最后第四个节点8交换,8到堆顶位置与它的左右孩子节点小,8与它最大的左孩子节点23交换,交换后8到23位置,又与它的左节点22小(此时右节点不在范围内),8与22交换

交换后数组长度-1,接着堆顶23与最后一个数据即最后第五节点8交换,8到堆顶位置与左右孩子节点小,8与它最大的左孩子节点22交换

交换后数组长度-1,接着堆顶22与最后一个数据即最后第六个节点10交换

交换后数组长度-1,最后堆顶10与最后一个数据即最后第七个节点8交换

这样堆排序就完后了。

代码如下:

public class Sort {
    public static void main(String[] args) {
        int[] data = { 31, 23, 89, 10, 47, 68, 8, 22 };
        heapSort(data);
        showData(data);
    }

    // 打印数组
    private static void showData(int[] data) {
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + " ");
        }
    }

    // 堆排序 筛选算法
        private static void sift(int[] data, int i, int n) {
            int j, temp;

            temp = data[i];
            j = 2 * i + 1;
            while (j < n) {
                if (j + 1 < n && data[j + 1] > data[j]) // 在左右孩子中找最大的
                    j++;

                if (data[j] <= temp)
                    break;

                data[i] = data[j]; // 把较大的子结点往上移动,替换它的父结点
                i = j;
                j = 2 * i + 1;
            }
            data[i] = temp;// 堆顶记录填入适当位置
        }

        // 创建大项堆算法
        private static void buildHeap(int[] data, int n) {
            int i;
            for (i = n / 2 - 1; i >= 0; i--) {// 建立初始堆
                sift(data, i, n);
            }
        }

        // 堆排序
        private static void heapSort(int[] data) {
            int n = data.length;
            int i;
            int t;
            buildHeap(data, n);
            for (i = n - 1; i >= 1; i--) {
                t = data[i];// 将堆顶记录与最后一个记录交换
                data[i] = data[0];
                data[0] = t;
                sift(data, 0, i);// 调整堆
            }
        }
}

排序八:归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。

归并操作的基本思想:

1.申请一个临时数组,使其大小为要排序的数组一样大,排序时用来临时存放数据。

2.将要排序的数据以中点分成两组数据各自排序,各自排序也是以各自的中点分成两组数据直到递归分割数据到基本单位;

3.从两个已经排序序列的起始位置比较,选择相对小的元素放入临时数组里,并比较下一位置的数据,直到超出序列尾。

4.将另一序列剩下的所有元素直接复制到临时数组,最后临时数组数据全部赋值给要排序的数组,这样就排序完成了。

下面以31, 23, 89, 10, 47, 68, 8, 22为例:

分为两个子序列31, 23, 89, 10  和  47, 68, 8, 22

再分别将序列31, 23, 89, 10分为两个子序列31, 23和89, 10,序列47, 68, 8, 22分为两个子序列47, 68和8, 22。

分别对这些子序列排序,排序后对两个大子序列进行排序:

这样归并排序就完成了。

代码

public class Sort {
    public static void main(String[] args) {
        int[] data = { 31, 23, 89, 10, 47, 68, 8, 22 };
        mergeSort(data);
        showData(data);
    }

    // 打印数组
    private static void showData(int[] data) {
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + " ");
        }
    }
        // 归并排序
        public static void mergeSort(int[] list) {
            int length = list.length;
            int[] temp = new int[length];// 临时数组
            recMergeSort(list, temp, 0, length - 1);
        }

        // 递归分割数据到基本单位
        private static void recMergeSort(int[] list, int[] temp, int low, int upper) {
            if (low == upper) {
                return;
            } else {
                int mid = (low + upper) / 2;
                recMergeSort(list, temp, low, mid);
                recMergeSort(list, temp, mid + 1, upper);
                merge(list, temp, low, mid + 1, upper);
            }
        }

        // 归并操作将基本单位归并成整个有序的数组
        private static void merge(int[] list, int[] temp, int left, int right, int last) {
            int j = 0;
            int lowIndex = left;
            int mid = right - 1;
            int n = last - lowIndex + 1;
            while (left <= mid && right <= last) {
                if (list[left] < list[right]) {
                    temp[j++] = list[left++];
                } else {
                    temp[j++] = list[right++];
                }
            }
            while (left <= mid) {
                temp[j++] = list[left++];
            }
            while (right <= last) {
                temp[j++] = list[right++];
            }
            for (j = 0; j < n; j++) {
                list[lowIndex + j] = temp[j];
            }
        }

}
时间: 2024-10-13 23:28:20

排序算法笔记二的相关文章

排序算法--(二)

选择排序 直接选择排序: 选择排序,每一趟找到一个最小(大)值,每一趟遍历的数据减少一次. template <typename T> void SelectSort(T a[],int length) { T temp; for (int i=0;i<length;i++) { int k =i; for (int j=i+1;j<length;j++) { if (a[j]<a[k]) k=j; //这里只是对比它小的元素中最小的位置进行标记,每次相当于最多移动一次. }

学习排序算法(二):Pairwise方法之Ranking SVM

学习排序算法(二):Pairwise方法之Ranking  SVM 1. Pairwise方法的基本思想 Pairwise考虑了文档顺序的关系.它将同一个query的相关文档其中起来,把任意两个文档组成一个pair.我们研究就是以这个pair文档对来研究的. 2. Ranking SVM 在测试的时候,我们就把一个query的所有结果先构建所有可能的pair,然后输入到学习到的模型中,得到每个pair的相对顺序.根据推算就可以得到所有搜索结果的排序了. 典型的Pairwise方法就有rankin

学习排序算法(二):Pairwise方法之RankNet

学习排序算法(二):Pairwise方法之RankNet 前面一篇博文介绍的Ranking SVM是把LTR问题转化为二值分类问题,而RankNet算法是从另外一个角度来解决,那就是概率的角度. 1. RankNet的基本思想 RankNet方法就是使用交叉熵作为损失函数,学习出一些模型(例如神经网络.决策树等)来计算每个pair的排序得分,学习模型的过程可以使用梯度下降法. 2. 方法流程 首先,我们要明确RankNet方法的目的就是要学习出一个模型,这个模型就是给文档算法的函数f(d, w)

排序算法总结二

本文接排序算法总结一 3. 冒泡排序 冒泡排序的基本思想:以正序排列为例,我们首先要将最大的数沉到最底下,从第一个数开始,比较相邻的两个数,如果为逆序则交换这两个数,重复这个操作直到倒数第二个数,此时最大的数已沉到最底下:然后再从第一个数开始,用同样的方法将次大的数沉到次底下,重复这个过程直到排序成功.代码如下: void PaoSort1(vector<int>& a) { int length = a.size(); for (int i = 0; i < length -

排序算法(二)——选择排序及改进

选择排序 基本思想 冒泡排序中有一个缺点,比如,我们比较第一个数a1与第二个数a2的时候,只要a1比a2大就会交换位置,但是我们并不能确定a2是最小的元素,假如后面还有比它更小的,该元素还会与a2再次进行交换,而且这种交换有可能发生多次才能确定a2的最终位置. 选择排序可以避免这种耗费时间的交换操作,从第一个元素开始,扫描整个待排数组,找到最小的元素放之后再与第一个元素交换位置,然后再从第二个元素开始,继续寻找最小的元素与第二个元素交换位置,依次类推. java实现 //选择排序 public

常见的排序算法(二) 选择排序

选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完. 选择排序: 思想 n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果: ①初始状态:无序区为R[1..n],有序区为空. ②第1趟排序 在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区

常见的排序算法(二) 交换排序(冒泡排序,快速排序)

今天,给大家带来的是交换排序. 首先,我们来了解一下什么叫交换排序.所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动.   那么接下来,我们来看一下.冒泡排序. 冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法. 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排

排序算法(二)--选择排序

选择排序 选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n2) 的时间复杂度.所以用到它的时候,数据规模越小越好.唯一的好处可能就是不占用额外的内存空间了吧. 1. 算法步骤 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾. 重复第二步,直到所有元素均排序完毕. 2. 动图演示 3. C++代码实现 /* 选择排序,升序排列 O(N^2)时间复杂度,O(1)空间复杂度 */ vector<int>

数据结构 排序算法 笔记

/* 冒泡排序 原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换, 这样一趟过去后,最大或最小的数字被交换到了最后一位, 然后再从头开始进行两两比较交换,直到倒数第二位时结束,其余类似看例子 例子为从小到大排序 */ # include <stdio.h> # define LEN 6 void bubble_sort(int *, int); int main(void) { int arry[LEN] = {6, 2, 4, 1, 5, 9}; int i; for (