常用排序算法代码总结
讲排序的时间空间复杂度什么的网上一大推,这里就不写了,仅仅写了这些算法的实现。 ///////////////////////////////////////////////////////////////////////////////////////////////////////////// //(1)并归排序 //归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的, //那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了? //可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时, //可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。 template<typename T> void merge(T A[], T B[], T C[], int lengthA, int lengthB) //用来就将两个有序的数组合并成一个 { int indexA = 0, indexB = 0, indexC = 0; //indexA是数组A下标,indexB是数组B的下标,indexC是新数组C的下标 while (indexC < lengthA + lengthB) //当indexC的大小是A B数组的和的时候,说明A B数组已经比较完了,出循环 { if (A[indexA] < B[indexB]) //如果A数组的当前值比较小,将其放在C中。 { C[indexC++] = A[indexA++]; } else //A B数组的各个值比较,将小的先放在C中,合并完后,C是一个有序的数组 { C[indexC++] = B[indexB++]; } if (indexA == lengthA) //说明A数组的数字已经全部遍历完了,然后把B剩下的数全部放进C { while (indexB < lengthB) { C[indexC++] = B[indexB++] } } else if (indexB == lengthB) //说明B数组的数字已经全部遍历完了,然后把A剩下的数全部放进C { while (indexA < lengthA) { C[indexC++] = A[indexA++]; } } } } template<typename T> void mergSort(T A[], int n)//将原数组分解,分而治之,每次折半 { if (n > 1) { int i = n / 2; int j = n - n / 2; T B[n / 2]; T C[n - n / 2]; for (int k = 0; k < i; ++K) B[k] = A[k]; for (int k = 0; k < j; ++K) C[k] = A[k + i]; mergSort(B, i); mergSort(C, j); merge(B, C, A, i, j); } } //(2) 快排 //快速排序是找出一个元素(平时常用作为基准的就是第一个,最后一个,中间)作为基准(pivot), 然后对数组进行分区操作, //使基准左边元素的值都不大于基准值, 基准右边的元素值 都不小于基准值, //如此作为基准的元素调整到排序后的正确位置。递归快速排序, //将其他n - 1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。 //所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。 template<typename T> void quickSort(T data[], int min, int max) { int m_min = min; int m_max = max; T mid = data[min]; //将第一个作为基准 if (min < max) //前后的指针未相遇 { while (m_min < m_max) { while (m_min < m_max && data[m_max]>mid) //从后向前移动max指针。如果当前值比基准值大,指针继续向前移动,后面是大数。 { //每次判断m_min < m_max,因为指针在一直移动。 --m_max; } data[m_min] = data[m_max]; //出了循环,说明当前值比基准小,所以把当前值放了基准的位置 while (m_min < m_max && data[m_min] <= mid)//从前向后移动min指针。如果当前值比基准值小,指针继续向后移动,前面是小数。 { ++m_min; } data[m_max] = data[m_min]; } data[m_min] = mid; //刚开始选择的基准,就在这个位置 quickSort(data, min, m_min - 1); quickSort(data, m_min + 1, max); } } //(3)插入排序 //插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。 template<typename T> void insertSort(T data[], int size) { int i = 0; int j = 0; T key; for (j = 1; j < size; ++j) { key = data[j]; i = j - 1; while (i >= 0 && key < data[i])//这里是找当前值的正确位置,比当前值大的话,将当前值后移,直到找到合适位置 { data[i + 1] = data[i]; --i; } data[i + 1] = key; //找到正确位置了。原先这个位置的值已经移动到了后面,将key放入 } } //(4)冒泡排序 //相邻两个数比较,依次交换。 template<typename T> void BubbleSort(T data[], int size) { bool flag = false; int i = 0; int j = 0; do { for (j = 0; j < size - i - 1; ++j) { if (data[j]>data[j + 1]) { //这里不用异或^(data[j] = data[j] ^ data[j + 1])是因为异或能用于double data[j] = data[j] + data[j + 1]; data[j + 1] = data[j] - data[j + 1]; data[j] = data[j] - data[j + 1]; flag = true;//防止数字已经有序,程序会多执行几次循环 } } ++i; } while (i < size && true == flag); } //(5)堆排序 //利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。 // //其基本思想为(大顶堆): //1)将初始待排序关键字序列(R1, R2....Rn)构建成大顶堆,此堆为初始的无序区; //2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1, R2, ......Rn - 1)和新的有序区(Rn), 且满足R[1, 2...n - 1] <= R[n]; //3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1, R2, ......Rn - 1)调整为新堆, //然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1, R2....Rn - 2)和新的有序区(Rn - 1, Rn)。 //不断重复此过程直到有序区的元素个数为n - 1,则整个排序过程完成。 template<typename T> void keepHeap(T data[], int heap_size, int k)//每个父节点和自己的叶子结点比较,看是否满足堆的性质 { int left = 2 * k + 1; //堆的深度,因为下标成从0开始的,所以左孩子是加1,右孩子加2 int right = 2 * k + 2; int largest = k; if (left<heap_size && data[left]>data[k]) { largest = left; } if (right<heap_size && data[right]>data[largest]) { largest = right; } if (largest != k) //上面两个if是为了找到左右孩子里面,较大的数,然后和当前父节点交换 { data[k] = data[largest] + data[k]; data[largest] = data[k] - data[largest]; data[k] = data[k] - data[largest]; keepHeap(data, heap_size, largest); //选择当前最大的值,继续看是否符合堆的性质 } } template<typename T> void buildHeap(T data[], int heap_size) { int i = heap_size / 2 - 1; while (i >= 0) { keepHeap(data, heap_size, i); //从小往上,建立堆 --i; } } template<typename T> void heapSort(T data[], int n) { int heap_size = n; buildHeap(data, heap_size); //建立堆 for (int i = heap_size - 1; i > 0; --i) //最后一个叶子和堆顶交换 { data[0] = data[0] + data[i]; data[i] = data[0] - data[i]; data[0] = data[0] - data[i]; --heap_size; keepHeap(data, heap_size, 0); } } //(6)希尔排序 //希尔排序的实质就是分组插入排序,该方法又称缩小增量排序 //基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行(直接插入,冒泡)排序, //然后依次缩减增量再进行排序, //待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次排序。因为(直接插入,冒泡)某些排序在元素基本有序的情况下高效 //(接近最好情况), //效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。 template<typename T> void shellSort(T *A, int n) { int i, j, gap = n; int counter = 1; bool flag; while (gap > 1)//增量小于一时,对全体进行排序 { gap = gap / 2; counter = 1; do{ flag = false; for (i = 0; i <= n - gap - counter; ++i)//用冒泡排序对一个小子序列进行排序 { j = i + gap; if (A[i] > A[j]) { A[i] = A[i] + A[j]; A[j] = A[i] - A[j]; A[i] = A[i] - A[j]; flag = true; } } ++counter; } while (counter < n && flag == 1); } } //(7)选择排序 //每一趟排序从序列中未排好序的那些元素中选择一个值最小(最大)的元素,然后将其与这些未排好序的元素的第一个(最后一个)元素交换位置。 template<typename T> void selectionSort(T data[], int n) { int i = 1, j; int max; //记录最大值 while (i < n) { max = n - i; for (j = 0; j < n - i + 1; ++j) //为了找最大值 { if (data[j]>data[max]) max = j; } if (max != n - i) //判断是否找到新的最大值,进行交换 { data[max] = data[max] + data[n - i]; data[n - i] = data[max] - data[n - i]; data[max] = data[max] - data[n - i]; } ++i; } } //(8)计数排序 //计数排序是一个类似于桶排序的排序算法,其优势是对已知数量范围的数组进行排序。 //初始化一个计数数组,大小是输入数组中的最大的数。 //遍历输入数组,遇到一个数就在计数数组对应的位置上加一。例如:遇到5,就将计数数组第五个位置的数加一。 //把计数数组直接覆盖到输出数组(节约空间)。 template<typename T> void countingSort(T *a, int n) { int k;//数组B长度 int dx = 0;//反向填充目标数组时的位置标示 T max = a[0];//待排序数组的最大值 T min = a[0];//待排序数组的最小值 for (int i = 1; i < n; ++i)//找到最大最小值,然后算应该分配多大空间 { if (a[i]>max) max = a[i]; else if (a[i] < min) min = a[i]; } k = max - min + 1;//根据最大最小值计算K int *b = new int[k]; memset(b, 0, k*sizeof(int)); //初始化0 for (int i = 0; i < n; ++i) //记录数字 ++b[a[i] - min]; for (int i = 0; i < k; ++i) { while (b[i]--) //填充数组 { a[dx++] = i + min; } } delete[] b; }
#C++工程师##算法工程师#