常见排序算法总结
2012-07-16 10:15
351 查看
参考了大牛的代码,自己理解整理。vs2008运行正确,如发现有误,请各位大牛指正!
// Sort.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> using namespace std; const int len = 100; class CSort { public: CSort(); ~CSort(); void swap(int &a,int &b); virtual void sort()=0; //虚函数 friend ostream& operator<<(ostream& out,const CSort& csort); //流操作符重载,定义为友元,方便类之间的数据共享 protected: int *arr; int length; };//注意此处应该有分号 CSort::CSort():length(len) //构造函数,初始化数组 { arr = new int[length]; for (int i=0; i<length;i++) { arr[i] = rand()%1000;//产生随机数 } } CSort::~CSort() //析构函数,释放申请的内存空间 { delete[] arr; arr = NULL; } void CSort::swap(int &a,int &b) { int temp = a; a = b; b = temp; } ostream& operator<<(ostream& cout,const CSort& csort) { for (int i=0; i<csort.length;i++) { cout<<csort.arr[i]<<" "; } cout<<endl; return cout; //注意 } //插入排序 从小到大 class CInsertSort : public CSort { public: void sort(); }; /************************************************************************/ /* 直接插入排序: 初始状态认为第一个元素是一个有序的序列,从第二个元素开始逐个插入到有序序列中 的正确位置。当有序序列的最后一个元素比当前元素(用key记录)大,则需要插入,这时 需要移动有序序列中的部分元素,腾出当前元素应该插入的正确位置;否则不插,当前 元素不动。 时间复杂度:O(n*n) 最好情况:O(n) */ /************************************************************************/ void CInsertSort::sort() { int i,j; int key = 0;//当前待插入的数 for (i=1; i<length; i++) { if (arr[i-1] > arr[i])//当有序序列的最后一个比当前大,则需要插入,否则不插 { key = arr[i]; for (j = i-1;j>=0 && arr[j]>key;j--)//寻找待插入数的正确位置 { arr[j+1] = arr[j];//后移 } arr[j+1] = key; } } } //折半插入排序 class CBInsertSort : public CSort { public: void sort(); }; /* 折半插入排序: 与直接插入排序所不同的是,在有序序列中找到当前元素的正确位置时 采用折半查找的方式,而不是挨个儿比较。 时间复杂度:O(n*logn) */ void CBInsertSort::sort() { int i,j; for (i=1; i<length; i++) { int low = 0; int high = i-1; int key = arr[i]; //通过折半找到插入的位置 while(high >= low)//取等号的时候仍要执行 { int mid = (low + high)/2; if (arr[i] > arr[mid]) { low = mid + 1; } else { high = mid -1; } } //找到插入位置high+1后,移位 /*for (j=i-1;j>=high+1;j--) { arr[j+1] = arr[j]; } arr[high+1] = key;*/ //找到插入位置low后,移位 for (j=i-1;j>=low;j--) { arr[j+1] = arr[j]; } arr[low] = key; } } //希尔排序 class CShellInsertSort : public CSort { public: void sort(); private: void shellinsert(int inc); }; /** 希尔排序: 又称缩小增量的插入排序。 增量一定的时候,将直接插入排序中的1改为增量inc即可。 相比直接插入排序好处在哪? 时间复杂度:O(n^1.3) */ void CShellInsertSort::sort() { int a[3]={5,3,1};//增量 for (int i=0; i<3; i++) { shellinsert(a[i]); } } void CShellInsertSort::shellinsert(int inc)//希尔排序,将直接插入排序中的1改为增量inc即可 { int i,j; int key = 0;//当前待插入的数 for (i=inc; i<length; i++) { if (arr[i-inc] > arr[i])//当有序序列的最后一个比当前大,则需要插入,否则不插 { key = arr[i]; for (j = i-inc;j>=0 && arr[j]>key;j-=inc)//寻找待插入数的正确位置 { arr[j+inc] = arr[j];//后移 } arr[j+inc] = key; } } } //冒泡排序 class CBubbleSort : public CSort { public: void sort(); }; /** 冒泡排序: 比较长度减1趟,每趟都是从第一个元素开始依次比较,如果前者比后者大,则交换位置 每趟找到一个正确的位置,最大值、次大值。。。 时间复杂度:O(n^2) 最好情况为:O(n) */ void CBubbleSort::sort() { //bool change = true;//这样设置的目的是什么?可以减少比较次数吗? for (int i=1; i<=length-1; i++) { //change = false;//设置排好 for (int j=0; j<length-i; j++) { if (arr[j] > arr[j+1]) { //change = true;//需要调换位置 swap(arr[j],arr[j+1]); } } } } //快速排序 class CFastSort : public CSort { public: void sort(); private: int partition(int low, int high);//进行一次划分,找到一个确定的位置 void fastsort(int low, int high);//枢轴元素左右两部分分别进行快速排序 }; /** 快速排序: 初始状态将第一个元素作为枢轴元素,进行一次划分,找到它的确定位置 左半部分的元素都比枢轴元素小,右半部分的元素都比枢轴元素大 然后分别递归的对左右两部分的元素进行快速排序。 */ void CFastSort::sort() { fastsort(0,length-1); } /** 找到枢轴元素的位置,对数组进行划分。 设置两个游标:low,high.如果游标high对应的元素大于或等于枢轴元素,则游标high减1;否则将该值赋洞口 如果游标low对应的元素小于或等于枢轴元素,则游标low加1;否则将该值赋给洞口 当low=high时循环结束,它们所指示的位置就是枢轴元素所在的位置。 时间复杂度:O(n*logn) 最坏情况:O(n^2) */ int CFastSort::partition(int low, int high) { int key = arr[low];//枢轴元素初始化为第一个元素 为了方便理解,可以成为挖洞 while (low < high) { while (low < high && arr[high] >= key) { high--; } arr[low] = arr[high];//找到第一个比key小的元素 while (low < high && arr[low] <= key) { low++; } arr[high] = arr[low];//找到第一个比key大的元素 } arr[low] = key; return low; } void CFastSort::fastsort(int low, int high) { if (low < high) { int keypos = partition(low,high); fastsort(low,keypos-1); fastsort(keypos+1,high); } } //堆排序 从小到大 初始堆为大根堆 class CHeapSort : public CSort { public: void sort(); private: void heapsort(); //堆排序 void adjustheap(int s,int w); //对堆进行调整 s表示待调整的位置下标,w表示待排序的元素个数 }; void CHeapSort::sort() //实现父类的虚函数 { heapsort(); } /** 堆排序: 初始状态按照完全二叉树的结构依次排列形成一个初始堆。然后从第一个非终端节点开始到根结点 依次调整以该结点为根的子树,形成一个大根堆。之后将堆顶元素和最后一个元素交换,输出堆顶元素。 接着调整新形成的堆,注意这时是直接从根结点开始调整堆的。然后将堆顶元素和最后一个元素交换。。。 其中有个很重要的调整堆的操作。整个排序的过程,实际上是在数组中进行的。 时间复杂度:O(n*logn) */ void CHeapSort::heapsort() { //首先从第一个非终端节点开始调整,初始化为大根堆 for (int i=length/2-1; i>=0; i--) { adjustheap(i,length); } for (int j=length-1; j>0; j--) { swap(arr[0],arr[j]); //将堆顶元素将最后一个元素交换 adjustheap(0,j); //交换后,重新调整堆为大根堆 } } /** 调整堆的过程: 有两个参数:s表示待调整的位置下标,w表示待排序的元素个数 局部变量i表示的是最大值的下标 用key记录待调整的元素,比较时是从它的左孩子开始的, 注意要判断待调整的元素是否有两个孩子。如果它比孩子大,则不需要调整 否则将较大的孩子的值赋给待调整的元素。s变为该孩子的下标,继续比较。 最后找到待调整元素的正确位置。调整结束 */ void CHeapSort::adjustheap(int s,int w) { int key = arr[s];//key记录待调整的元素 for (int i=2*s+1; i<w; i=2*i+1) { if (i<w-1 && arr[i]<arr[i+1]) //下标i表示的是最大值的下标,i<m-1:保证其有左右孩子时,才进行左右孩子的比较 { i++; } if (key>arr[i]) //比孩子都大,不需要调整 { break; } arr[s]=arr[i]; s=i; } arr[s]=key; } //选择排序 class CSelectionSort : public CSort { public: void sort(); }; /* 选择排序: 比较长度减1次,每次找到一个最小值,和未排序序列中的第一个元素(即i所记录的)进行交换 用一个下标记录最小值的下标 时间复杂度:O(n^2) */ void CSelectionSort::sort() { for (int i=0; i<length-1; i++) { int minpos = i;//minpos表示最小值的下标 for (int j = i+1; j<length; j++) { if (arr[j] < arr[minpos]) { minpos = j;//遍历一趟找到最小值的下表 } } if (minpos!=i) { swap(arr[i],arr[minpos]);//注意此处是交换 } } } //归并排序 class CMergingSort : public CSort { public: void sort(); private: void mergesort(int left,int right); void merge(int left,int mid,int right); }; void CMergingSort::sort() { mergesort(0,length-1); } /* 归并排序: 采用递归的思想,从中间将数组划分为两部分,然后分别递归的对两部分进行归并排序 然后将两部分排好序的合并到一起,完成排序。 时间复杂度:O(n*logn) */ void CMergingSort::mergesort(int left,int right) { if (left<right) { int mid = (left+right)/2; mergesort(left,mid); //左半部分排好序 mergesort(mid+1,right); //右半部分排好序 merge(left,mid,right); //左右两部分排序 } } /* 归并两个排好序的部分的过程: 申请一个临时数组,挨个儿比较两个部分里的元素,先将小的元素加入到临时数组中 最后将临时数组中的元素复制到原数组中即可 */ void CMergingSort::merge(int left,int mid,int right) { int i=left; int j=mid+1; int *tem = new int[right-left+1];//申请和数组大小相等的内存空间 临时空间 int k=0; while (i<=mid && j<=right) { if (arr[i] < arr[j]) { tem[k++]=arr[i++]; } else { tem[k++]=arr[j++]; } } while (i<=mid) { tem[k++]=arr[i++]; } while (j<=right) { tem[k++]=arr[j++]; } for (i=0,j=left;j<=right;i++,j++) { arr[j]=tem[i]; } delete[] tem; } int _tmain(int argc, _TCHAR* argv[]) { CSort *pcsort; //pcsort = new CInsertSort(); //插入排序 //pcsort = new CBInsertSort(); //折半插入排序 //pcsort = new CShellInsertSort(); //希尔排序 //pcsort = new CBubbleSort(); //冒泡排序 //pcsort = new CFastSort();//快速排序 //pcsort = new CSelectionSort();//选择排序 //pcsort = new CHeapSort();//堆排序 pcsort = new CMergingSort();//归并排序 cout<<"排序前:"<<endl; cout<<*pcsort; pcsort->sort(); cout<<"排序后:"<<endl; cout<<*pcsort; system("pause"); return 0; }
相关文章推荐
- 常见排序算法总结(基于C++实现)
- 常见基础排序算法总结及java代码
- 【超强总结】常见排序算法C++总结
- 常见排序算法总结
- 【转】常见排序算法总结
- 七种常见排序算法的总结
- 一些常见排序算法的总结
- 算法笔记--八个常见排序算法总结
- 几种常见的排序算法Java实现总结
- 常见的几种排序算法总结
- javascript常见排序算法总结
- 常见排序算法总结-C++实现
- 常见Sort排序算法总结,人生第一帖
- 常见面试排序算法总结
- 算法笔记--八个常见排序算法总结
- 面试常见基本题目总结及php实现(第一部分:排序算法)
- 常见排序算法总结
- 常见排序算法总结
- 11种常见排序算法总结(更新ING...)
- 常见排序算法总结