选择排序:
下面这个选择排序的思路:找出所有元素中最小的元素,然后依次按从小到大放进数组中
# include <iostream>
# include <algorithm>
using namespace std;
void selectionSort(int arr[], int n){
for(int i = 0 ; i < n ; i ++){
// 寻找[i, n)区间里的最小值
int minIndex = i;
for( int j = i + 1 ; j < n ; j ++ )
if( arr[j] < arr[minIndex] )
minIndex = j; //找到最小的元素的位置
swap( arr[i] , arr[minIndex] ); //和当前的数组的位置进行交换
}
int main() {
int a[10] = {10,9,8,7,6,5,4,3,2,1};
selectionSort(a,10);
for( int i = 0 ; i < 10 ; i ++ )
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
//此处用到了模板类,可以方便测试的时候进行任何数值去测试
template<typename T>
void selectionSort(T arr[], int n){
for(int i = 0 ; i < n ; i ++){
int minIndex = i;
for( int j = i + 1 ; j < n ; j ++ )
if( arr[j] < arr[minIndex] )
minIndex = j;
swap( arr[i] , arr[minIndex] );
}
}
插入排序:
此次插入排序的思路:从数组中依次找一个元素,然后把这个元素与之前已经排好序的n个元素作比较,如果前面的n个元素大于此元素,则*交换位置*,此算法未进行优化!!!
template<typename T>
void insertionSort(T arr[], int n){
for( int i = 1 ; i < n ; i ++ ) {
// 寻找元素arr[i]合适的插入位置
// 写法1
// for( int j = i ; j > 0 ; j-- )
// if( arr[j] < arr[j-1] )
// swap( arr[j] , arr[j-1] );
// else
// break;
// 写法2
for( int j = i ; j > 0 && arr[j] < arr[j-1] ; j -- )
swap( arr[j] , arr[j-1] );
}
return;
}
已优化的算法:即把交换改成比较,比较完前n个数,确定是在此位置才进行交换
// 写法3
T e = arr[i];
int j; // j保存元素e应该插入的位置
for (j = i; j > 0 && arr[j-1] > e; j--)
arr[j] = arr[j-1]; //关键是这一步,满足条件就往后移
arr[j] = e;//找到位置,交换
对于上面两种O(n^2)级别的排序方法,但是插入排序相对于选择排序是更好的,因为插入排序对于近乎有序的数组,时间复杂度可以达到O(n),这是比后面O(nlog2n)的算法的时间复杂度还要低的,所以在一般的情况下,在处理近乎有序的数值的时候,我们要选用插入排序!
归并排序:
自顶向下的归并排序,将数组分成一半,一层一层地分,每一层都是除以2来分,分完之后,从底向上对每一部分进行递归排序,形成了每一部分都是有序的数组,然后到最后一层时,我们要进行归并,这时候,我们需要把所有的数组复制一份(这里就占用了O(n)的空间复杂度),然后需要三个位置的索引,第一第二个索引分别指向两个有序的数组,第三个所以指向最终的数组,最后通过第一第二个索引的数值进行比较找到最小的那个元素,进而把元素赋值给第三个索引的值。
// 将arr[l...mid]和arr[mid+1...r]两部分进行归并
template<typename T>
void __merge(T arr[], int l, int mid, int r){
// 经测试,传递aux数组的性能效果并不好
T aux[r-l+1];
for( int i = l ; i <= r; i ++ )
aux[i-l] = arr[i]; //复制
int i = l, j = mid+1;
for( int k = l ; k <= r; k ++ ){
if( i > mid ) { arr[k] = aux[j-l]; j ++;}
else if( j > r ){ arr[k] = aux[i-l]; i ++;}
//上面两个条件判断,是先判断有无越界,如果比较完之后,任何一边都又剩余的元素,则要把剩余的元素复制到数组的后面
//在arr[l...mid]和arr[mid+1...r]两部分找最小值
else if( aux[i-l] < aux[j-l] ){ arr[k] = aux[i-l]; i ++;}
else { arr[k] = aux[j-l]; j ++;}
}
}
// 递归使用归并排序,对arr[l...r]的范围进行排序
template<typename T>
void __mergeSort(T arr[], int l, int r){
if( l >= r ) //递归出口
return;
int mid = (l+r)/2;
//对划分的每一部分进行排序
__mergeSort(arr, l, mid);
__mergeSort(arr, mid+1, r);
//对最后一部分进行排序
__merge(arr, l, mid, r);
}
template<typename T>
void mergeSort(T arr[], int n){
__mergeSort( arr , 0 , n-1 );
}
自底向上的归并排序:第一个for循环时对数组进行划分,第二层循环是对划分的两部分数组进行排序。
template <typename T>
void mergeSortBU(T arr[], int n){
// for( int sz = 1; sz <= n ; sz += sz )
// for( int i = 0 ; i+sz < n ; i += sz+sz )
// // 对 arr[i...i+sz-1] 和 arr[i+sz...i+2*sz-1] 进行归并
// __merge(arr, i, i+sz-1, min(i+sz+sz-1,n-1) );
// Merge Sort Bottom Up 优化
for( int i = 0 ; i < n ; i += 16 )
insertionSort(arr,i,min(i+15,n-1));
for( int sz = 16; sz <= n ; sz += sz )
for( int i = 0 ; i < n - sz ; i += sz+sz )
if( arr[i+sz-1] > arr[i+sz] )
__merge(arr, i, i+sz-1, min(i+sz+sz-1,n-1) );
}
快排:
先取一个基准,这个取基准的方法就是partition方法,并且返回基准的索引,这个索引就是基准的最终位置
// 对arr[l...r]部分进行partition操作
// 返回p,使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
template <typename T>
int __partition(T arr[], int l, int r){ //l是基准
T v = arr[l];
int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v //j是分界点
for( int i = l + 1 ; i <= r ; i ++ ) //i是当前访问的元素
if( arr[i] < v ){ //主要是讨论小于的部分,大于的部分不用变
j ++;
swap( arr[j] , arr[i] ); //放到j的后面
}
swap( arr[l] , arr[j]); //最后一步,基准要放到j的位置
return j;
}
// 对arr[l...r]部分进行快速排序
template <typename T>
void __quickSort(T arr[], int l, int r){
if( l >= r )
return;
int p = __partition(arr, l, r);
//递归实现两部分的快排
__quickSort(arr, l, p-1 );
__quickSort(arr, p+1, r);
}
template <typename T>
void quickSort(T arr[], int n){
__quickSort(arr, 0, n-1);
}
堆排序
二叉堆是一棵完全二叉树,它也是一个树形结构,根节点是声明成1,i/2是根节点,2i是左孩子,2i+1是右孩子
shift up:
template<typename Item>
class MaxHeap{
private:
Item *data;
int count;
int capacity;
void shiftUp(int k){
while( k > 1 && data[k/2] < data[k] ){//k>1是考虑越界问题
swap( data[k/2], data[k] );
k /= 2; //更新k,依然找下一个父节点
}
}
public:
MaxHeap(int capacity){
data = new Item[capacity+1];
count = 0;
this->capacity = capacity;
}
~MaxHeap(){
delete[] data;
}
int size(){
return count;
}
bool isEmpty(){
return count == 0;
}
//在堆中插入一个元素,
void insert(Item item){
assert( count + 1 <= capacity );
data[count+1] = item;
count ++;
shiftUp(count);
}
public:
void testPrint(){
if( size() >= 100 ){
cout<<"Fancy print can only work for less than 100 int";
return;
}
if( typeid(Item) != typeid(int) ){
cout <<"Fancy print can only work for int item";
return;
}
cout<<"The Heap size is: "<<size()<<endl;
cout<<"data in heap: ";
for( int i = 1 ; i <= size() ; i ++ )
cout<<data[i]<<" ";
cout<<endl;
cout<<endl;
int n = size();
int max_level = 0;
int number_per_level = 1;
while( n > 0 ) {
max_level += 1;
n -= number_per_level;
number_per_level *= 2;
}
int max_level_number = int(pow(2, max_level-1));
int cur_tree_max_level_number = max_level_number;
int index = 1;
for( int level = 0 ; level < max_level ; level ++ ){
string line1 = string(max_level_number*3-1, ' ');
int cur_level_number = min(count-int(pow(2,level))+1,int(pow(2,level)));
bool isLeft = true;
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index ++ , index_cur_level ++ ){
putNumberInLine( data[index] , line1 , index_cur_level , cur_tree_max_level_number*3-1 , isLeft );
isLeft = !isLeft;
}
cout<<line1<<endl;
if( level == max_level - 1 )
break;
string line2 = string(max_level_number*3-1, ' ');
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index_cur_level ++ )
putBranchInLine( line2 , index_cur_level , cur_tree_max_level_number*3-1 );
cout<<line2<<endl;
cur_tree_max_level_number /= 2;
}
}
private:
void putNumberInLine( int num, string &line, int index_cur_level, int cur_tree_width, bool isLeft){
int sub_tree_width = (cur_tree_width - 1) / 2;
int offset = index_cur_level * (cur_tree_width+1) + sub_tree_width;
assert(offset + 1 < line.size());
if( num >= 10 ) {
line[offset + 0] = '0' + num / 10;
line[offset + 1] = '0' + num % 10;
}
else{
if( isLeft)
line[offset + 0] = '0' + num;
else
line[offset + 1] = '0' + num;
}
}
void putBranchInLine( string &line, int index_cur_level, int cur_tree_width){
int sub_tree_width = (cur_tree_width - 1) / 2;
int sub_sub_tree_width = (sub_tree_width - 1) / 2;
int offset_left = index_cur_level * (cur_tree_width+1) + sub_sub_tree_width;
assert( offset_left + 1 < line.size() );
int offset_right = index_cur_level * (cur_tree_width+1) + sub_tree_width + 1 + sub_sub_tree_width;
assert( offset_right < line.size() );
line[offset_left + 1] = '/';
line[offset_right + 0] = '\\';
}
};
shif down:在取出元素的时候,要保持完全二叉树的形态
void shiftDown(int k){
while( 2*k <= count ){ //count是堆的数目
int j = 2*k; // 在此轮循环中,data[k]和data[j]交换位置
if( j+1 <= count && data[j+1] > data[j] )
j ++;
// data[j] 是 data[2*k]和data[2*k+1]中的最大值
if( data[k] >= data[j] ) break;
swap( data[k] , data[j] ); //这一步其实还是可以优化的,可以把swap改成是赋值操作,通过开辟新的空间来给满足条件的值一个一个赋值
k = j; //向下移动k的位置
}
}
Item extractMax(){
assert( count > 0 );
Item ret = data[1]; //取出根节点
swap( data[1] , data[count] );
count --;
shiftDown(1);
return ret;
}
来源:CSDN
作者:Professor麦
链接:https://blog.csdn.net/Professor11/article/details/103866731