插入,选择,交换,归并,基数排序
2014-01-05 14:12
411 查看
1. 插入排序
1.1 直接插入排序
(将后面的元素插入到前面已经排好的序列)
稳定;空间代价Θ(1),时间代价:Θ(n*n)
1.2 Shell 排序
(依次增量插入)
不稳定;空间代价Θ(1),时间代价:Θ(n*n)。Shell最好的增量,呈
2p3q 形式的一系列整数:1, 2, 3, 4, 6, 8, 9, 12
2. 选择排序
2.1 直接选择排序
(依次选择最小元素放到对应的位置)
不稳定;空间代价:Θ(1),时间代价:Θ(n2)
(先堆化,然后取最大元素然后继续调整为大根堆)
不稳定;空间代价为 Θ(1),总时间代价为 Θ(nlog n)
3. 交换排序
3.1 冒泡排序
(通过依次比较相邻的两个元素,让最小元素排序往左边移动直到到达它的合适位置)
稳定;空间代价:Θ(1),平均时间代价均为 Θ(n2)
(选择一个轴值,把数组分成左边比轴值小,右边比轴值大的两个数组,然后继续分治)
不稳定;平均空间代价:Θ(log
n)(递归调用使用到栈);平均时间代价:Θ(nlog n);
4. 归并排序
(将数组划分为两个子序列,分别对每个子序列归并排序,然后合并有序子序列)
稳定;空间代价:Θ(n),平均时间代价均为 Θ(nlog n)
5. 分配排序和基数排序
不需要进行纪录之间两两比较,需要事先知道记录序列的一些具体情况
5.1 桶排序
(记录元素分布区间[0,max),把数据放入对应的桶,并计数。然后从后往前读取数据,查询桶中计数,判断具体位置.)
稳定;总的时间代价为 Θ(m+n),空间代价Θ(m+n),m为区间个数 (适合于 m 比较小的情况)
5.2 基数排序
(原理同桶排序一样,只是当 m 很大时,将一个记录的值即排序码拆分为多个部分来进行比较)
稳定;空间代价:Θ(n+r),时间代价:Θ(d·(n+r))
1.1 直接插入排序
(将后面的元素插入到前面已经排好的序列)
稳定;空间代价Θ(1),时间代价:Θ(n*n)
/****************** 直接插入排序 ******************/ template <class Record> void ImprovedInsertSort (Record Array[], int n) { //Array[] 为待排序数组,n 为数组长度 Record TempRecord; // 临时变量 for (int i=1; i<n; i++) // 依次插入第 i 个记录 { TempRecord = Array[i]; //从 i 开始往前寻找记录 i 的正确位置 int j = i-1; //将那些大于等于记录 i 的记录后移 while ((j>=0) && (TempRecord < Array[j])) { Array[j+1] = Array[j]; //此时 j 后面就是记录 i 的正确位置,回填 j = j - 1; } Array[j+1] = TempRecord; } }
1.2 Shell 排序
(依次增量插入)
不稳定;空间代价Θ(1),时间代价:Θ(n*n)。Shell最好的增量,呈
2p3q 形式的一系列整数:1, 2, 3, 4, 6, 8, 9, 12
template <class Record> void ModInsSort(Record Array[], int n, int delta) { // 针对变化的增量而修改的插入排序算法,参数delta表示当前的增量 Record TempRecord; // 临时变量 for (int i=delta; i<n; i+=delta) // 对子序列中第i个记录排序 { TempRecord=Array[i]; int j = i-delta; //从i开始往前寻找记录i的正确位置 while (j>=0 && TempRecord < Array[j]) { Array[j+delta] = Array[j]; //将那些大于等于记录i的记录后移 j = j - delta; } Array[j+delta] = TempRecord; //此时j后面就是记录i的正确位置,回填 } } template <class Record> void ShellSort(Record Array[], int n) //Shell排序,Array[]为待排序数组,n为数组长度 { for (int delta=n/3; delta>=1; delta/=3) //增量delta每次除以3递减 for (int j=0; j<delta; j++) //分别对delta个子序列进行插入排序 ModInsSort(&Array[j], n-j, delta); //如果增量序列不能保证最后一个delta间距为1, 安排下面这个扫尾性质的插入排序 ModInsSort(Array, n, 1); }
2. 选择排序
2.1 直接选择排序
(依次选择最小元素放到对应的位置)
不稳定;空间代价:Θ(1),时间代价:Θ(n2)
/****************** 直接选择排序 ******************/ template <class Record> void SelectSort(Record Array[], int n) // 直接选择排序,Array[]为待排序数组,n为数组长度 { for (int i=0; i<n-1; i++) // 依次选出第i小的记录,即剩余记录中最小的那个 { int Smallest = i; // 首先假设记录i就是最小的 for (int j=i+1; j<n; j++) // 开始向后扫描所有剩余记录 if (Array[j] < Array[Smallest]) Smallest = j; // 如果发现更小的记录,记录它的位置 // 交换Array[i]和 Array[Smallest],第i小的记录到位 if (Smallest != i) { Record temp = Array[Smallest]; Array[Smallest] = Array[i]; Array[i] = temp; } } }2.2 堆排序
(先堆化,然后取最大元素然后继续调整为大根堆)
不稳定;空间代价为 Θ(1),总时间代价为 Θ(nlog n)
/****************** 堆排序 ******************/ template <class T> class MaxHeap { private: T* heapArray; //存放堆数据的数组 int CurrentSize; //当前堆中元素数目 int MaxSize; //堆所能容纳的最大元素数目 public: MaxHeap(T* array,int num,int max); virtual ~MaxHeap() {}; //析构函数 void BuildHeap(); bool isLeaf(int pos) const; //如果是叶结点,返回TRUE int leftchild(int pos) const; //返回左孩子位置 int rightchild(int pos) const; //返回右孩子位置 int parent(int pos) const; //返回父结点位置 bool Remove(int pos, T& node); //删除给定下标的元素 void SiftDown(int left); //筛选法函数,参数left表示开始处理的数组下标 void SiftUp(int position); //从position向上开始调整,使序列成为堆 bool Insert(const T& newNode); //向堆中插入新元素newNode void MoveMax(); //从堆顶移动最大值到尾部 T& RemoveMax(); //从堆顶删除最大值 }; template<class T> MaxHeap<T>::MaxHeap(T* array,int num,int max) { heapArray=array; CurrentSize=num; MaxSize=max; BuildHeap(); } template<class T> void MaxHeap<T>::BuildHeap() { for (int i=CurrentSize/2-1; i>=0; i--) SiftDown(i); } template<class T> bool MaxHeap<T>::isLeaf(int pos) const { return (pos>=CurrentSize/2)&&(pos<CurrentSize); } template<class T> int MaxHeap<T>::leftchild(int pos) const { return 2*pos+1; //返回左孩子位置 } template<class T> int MaxHeap<T>::rightchild(int pos) const { return 2*pos+2; //返回右孩子位置 } template<class T> int MaxHeap<T>::parent(int pos) const //返回父节点位置 { return (pos-1)/2; } template<class T> void MaxHeap<T>::SiftDown(int left) { //准备 int i= left; //标识父结点 int j= 2*i+1; //标识关键值较小的子结点 T temp=heapArray[i]; //保存父结点 //过筛 while(j<CurrentSize) { if((j<CurrentSize)&&(heapArray[j]<heapArray[j+1])) j++; //j指向右子结点 if(temp<heapArray[j]) { heapArray[i]=heapArray[j]; i=j; j=2*j+1; //向下继续 } else break; } heapArray[i]=temp; } template<class T> void MaxHeap<T>::SiftUp(int position) { //从position向上开始调整,使序列成为堆 int temppos=position; T temp=heapArray[temppos]; int parentpos=0; if(temppos>0) parentpos=parent(temppos); while((temppos>0)&&(heapArray[parentpos]<temp)) { heapArray[temppos]=heapArray[parentpos]; temppos=parentpos; parentpos=parent(temppos); } heapArray[temppos]=temp; } template<class T> bool MaxHeap<T>::Insert(const T& newNode) { //向堆中插入一个结点 if(CurrentSize==MaxSize) //堆空间已经满 return false; heapArray[CurrentSize]=newNode; SiftUp(CurrentSize); //向上调整 CurrentSize++; } template<class T> T& MaxHeap<T>::RemoveMax() { if(CurrentSize==0) { cout << "Can't Delete" << endl; return; } else { T temp=heapArray[0]; //取堆顶元素 heapArray[0]=heapArray[CurrentSize-1]; //堆末元素上升至堆顶 CurrentSize--; SiftDown(0); //从堆顶开始筛选 return temp; } } template<class T> void MaxHeap<T>::MoveMax() { if(CurrentSize==0) { return; //堆为空 } else { T temp=heapArray[0]; //取堆顶元素 heapArray[0]=heapArray[CurrentSize-1]; //堆末元素上升至堆顶 CurrentSize--; SiftDown(0); //从堆顶开始筛选 heapArray[CurrentSize]=temp; } return; } template<class T> bool MaxHeap<T>::Remove(int pos, T& node) { // 删除给定下标的元素 if((pos<0)||(pos>CurrentSize)) return false; heapArray[pos]=heapArray[--CurrentSize]; //指定元素置于最后 SiftUp(pos); //上升筛 SiftDown(pos); //向下筛 node=heapArray[CurrentSize]; return true; } //堆排序,Array[]为待排序数组,n为数组长度 template <class Record> void HeapSort(Record Array[], int n) { MaxHeap<Record> max_heap = MaxHeap<Record>(Array, n, n); //建堆 for (int i=0; i<n; i++) // 依次找出剩余记录中的最大记录,即堆顶 max_heap.MoveMax(); }
3. 交换排序
3.1 冒泡排序
(通过依次比较相邻的两个元素,让最小元素排序往左边移动直到到达它的合适位置)
稳定;空间代价:Θ(1),平均时间代价均为 Θ(n2)
/****************** 冒泡排序 ******************/ template <class Record> void BubbleSort(Record Array[], int n) // 优化的冒泡排序,Array[]为待排序数组,n为数组长度 { bool NoSwap; // 是否发生了交换的标志 for (int i=0; i< n-1; i++) { NoSwap = true; // 标志初始为真 for (int j=n-1; j>i; j--) if (Array[j] < Array[j-1]) // 判断(Array[j-1],Array[j])是否逆置 { Record temp = Array[j]; Array[j] = Array[j-1]; Array[j-1] = temp; NoSwap = false; // 发生了交换,标志变为假 } if (NoSwap) // 如果没发生过交换,表示已排好序,结束算法 return; } }3.2 快速排序
(选择一个轴值,把数组分成左边比轴值小,右边比轴值大的两个数组,然后继续分治)
不稳定;平均空间代价:Θ(log
n)(递归调用使用到栈);平均时间代价:Θ(nlog n);
/****************** 快速排序 ******************/ void swap(Record *Array, int &id1, int &id2) // 交换数组中两个下标的值 { Record temp = Array[id1]; Array[id1] = Array[id2]; Array[id2] = temp; } int SelectPivot(int left, int right) // 选择轴值,参数left,right分别表示序列的左右端下标 { return (left+right)/2; // 选择中间记录作为轴值 } template <class Record> int Partition(Record Array[], int left, int right) // 分割函数,分割后轴值已到达正确位置 { int l = left; // l为左指针,r为右指针 int r = right; Record TempRecord = Array[r]; // 将轴值存放在临时变量中 while (l != r) // 开始分割,l,r不断向中间移动,直到相遇 { // l指针向右移动,直到找到一个大于轴值的记录 while (Array[l] <= TempRecord && r > l) // 这里也可以把"<="改写为"<",反正不稳定 l++; if (l < r) // 若l,r尚未相遇,将逆置元素换到右边的空位 { Array[r] = Array[l]; r--; // r指针向左移动一步 } // r指针向左移动,直到找到一个小于等于轴值的记录 while (Array[r] > TempRecord && r > l) // 这里也可以把">"改写为">=",减少记录移动 r--; if (l < r) // 若l,r尚未相遇,将逆置元素换到左边的空位 { Array[l] = Array[r]; l++; // l指针向右移动一步 } } //end while Array[l] = TempRecord; // 把轴值回填到分界位置l上 return l; // 返回分界位置l } template <class Record> void QuickSort(Record Array[], int left, int right) // Array[]为待排序数组,left,right分别为数组两端 { if (right <= left) return; // 如果子序列中只有0或1个记录,就不需排序 int pivot = SelectPivot(left, right); // 选择轴值 swap(Array, pivot, right); // 分割前先将轴值放到数组末端 pivot = Partition(Array, left, right); // 分割后轴值已到达正确位置 QuickSort(Array, left, pivot-1); // 对轴值左边的子序列进行递归快速排序 QuickSort(Array, pivot +1, right); // 对轴值右边的子序列进行递归快速排序 }
4. 归并排序
(将数组划分为两个子序列,分别对每个子序列归并排序,然后合并有序子序列)
稳定;空间代价:Θ(n),平均时间代价均为 Θ(nlog n)
/****************** 归并排序 ******************/ template <class Record> void Merge(Record Array[], Record TempArray[], int left, int right, int middle) //归并过程 { for (int j=left; j<=right; j++) // 将数组暂存入临时数组 TempArray[j] = Array[j]; int index1=left; // 左边子序列的起始位置 int index2=middle+1; // 右边子序列的起始位置 int i=left; // 从左开始归并 while (index1 <= middle && index2 <= right) { //取较小者插入合并数组中 if (TempArray[index1] <= TempArray[index2]) // 为保证稳定性,相等时左边优先 Array[i++] = TempArray[index1++]; else Array[i++] = TempArray[index2++]; } while (index1 <= middle) // 只剩左序列,可以直接复制 Array[i++] = TempArray[index1++]; while (index2 <= right) // 与上个循环互斥,直接复制剩余的右序列 Array[i++] = TempArray[index2++]; } template <class Record> void MergeSort(Record Array[], Record TempArray[], int left, int right) { //两路归并排序,Array[]为待排序数组,left,right分别为数组两端 if (left < right) // 如果序列中只有0或1个记录,就不用排序 { int middle=(left+right)/2; //从中间划分为两个子序列 MergeSort(Array,TempArray,left,middle); //对左边一半进行递归 MergeSort(Array,TempArray,middle+1,right); //对右边一半进行递归 Merge(Array, TempArray, left, right, middle);// 进行归并 } }
5. 分配排序和基数排序
不需要进行纪录之间两两比较,需要事先知道记录序列的一些具体情况
5.1 桶排序
(记录元素分布区间[0,max),把数据放入对应的桶,并计数。然后从后往前读取数据,查询桶中计数,判断具体位置.)
稳定;总的时间代价为 Θ(m+n),空间代价Θ(m+n),m为区间个数 (适合于 m 比较小的情况)
/****************** 桶排序 ******************/ template <class Record> void BucketSort(Record Array[], int n, int max) { // 桶式排序,Array[]为待排序数组,数组长度为n,所有记录都位于区间[0,max)上 int* TempArray = new Record ; // 临时数组 int* count = new int[max]; // 小于或等于i的元素个数 int i; for (i=0; i<n; i++) // 把序列复制到临时数组 TempArray[i] = Array[i]; for (i=0; i<max; i++) // 所有计数器初始都为0 count[i] = 0; for (i=0; i<n; i++) // 统计每个取值出现的次数 count[Array[i]]++; for (i=1; i<max; i++) // 统计小于等于i的元素个数 count[i] = count[i-1]+count [i]; for (i=n-1; i>=0; i--) // 从尾部开始按顺序输出有序序列,保证排序的稳定性 Array[--count[TempArray[i]]] = TempArray[i]; }
5.2 基数排序
(原理同桶排序一样,只是当 m 很大时,将一个记录的值即排序码拆分为多个部分来进行比较)
稳定;空间代价:Θ(n+r),时间代价:Θ(d·(n+r))
/****************** 基数排序 ******************/ template <class Record> void RadixSort(Record Array[], int n, int d, int r) { // 数组实现的基数排序,n为数组长度,d为排序码个数,r为基数 // 如果对数字排序,d为最大数的位数,r一般为10 Record *TempArray =new Record ; // 辅助排序的临时数组 int* count = new int[r]; // 桶容量计数器,count[i] 存储了第i个桶中的记录数 int i,j,k; int Radix = 1; // 模保持进位,用来取Array[j]的第i位排序码 for (i=0; i<d; i++) // 分别对第i个排序码进行分配 { for (j=0; j<r; j++) // 初始计数器均为0 count[j] = 0; for (j=0; j<n; j++) // 统计每个桶中的记录数 { k = (Array[j] / Radix) % r; // 取Array[j]的第i位排序码 count[k]++; // 相应计数器加1 } for (j=1; j<r; j++) // 将TempArray中的位置依次分配给r个桶 count[j] = count[j-1] + count[j]; for (j=n-1; j>=0; j--) // 从数字尾部,把记录收集到相应桶 { k = (Array[j] / Radix ) % r; // 取Array[j]的第i位排序码 count[k]--; // 使用了第k个桶的一个位置,桶剩余量的计数器减1 TempArray[count[k]] = Array[j]; } for (j=0; j<n; j++) // 将临时数组中的内容复制到Array中 Array[j] = TempArray[j]; Radix *= r; // 往左进一位,修改模Radix } }
相关文章推荐
- 九大排序算法及其实现- 插入.冒泡.选择.归并.快速.堆排序.计数.基数.桶排序.堆排序
- java算法之 排序(插入、交换、选择、归并、分配排序)
- 八大排序算法:插入(2:插入、希尔)、选择(2:选择、堆)、交换(2:冒泡、快速)、归并、基数
- 插入,归并,快速,冒泡,shell,选择排序 用c实现
- C++实现各种基础排序(冒泡、选择、快排、插入、堆排、希尔、归并)
- 八种排序算法(直接插入,希尔,直接选择,堆,冒泡,快排,归并,基数)
- Java-十种内部排序实现(选择,冒泡,插入,希尔,堆,归并,快速,基数,计数,桶)及代码下载
- 各种排序算法(冒泡、选择、快排、插入、希尔、堆排、归并、计数、基数)
- 【Java】八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序 、快速排序、归并排序、堆排序和LST基数排序
- 数据结构 27 排序 排序-选择 插入 冒泡 希尔 快速归并 6种排序
- 排序算法总结(插入、Shell、选择、堆排序、冒泡、快速、归并、基数)
- 选择,交换,插入的基本排序
- 插入排序、冒泡排序、选择排序、希尔排序、快速排序、归并排序、堆排序和LST基数排序——JAVA实现
- java实现七大排序(插入,冒泡,选择,快速,堆,希尔,归并)
- C++数据结构 排序 二分 插入 冒泡 基数 归并 直选 快排 希尔 堆排序
- 六种排序方法的学习(直接插入、希尔、冒泡、快速、选择、归并)
- 10种排序算法总结(冒泡、选择、插入、希尔、归并、快速、堆、拓扑、锦标赛、基数)
- 链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)【转载】
- 数据结构 学习笔记(十):排序(上):简单排序(冒泡,插入),希尔 / 选择 / 堆 / 归并 排序
- 简单排序总结:选择、插入、交换