排序算法学习

排序算法学习

本文包含:
1.冒泡排序
2.及时终止的冒泡排序
3.计数排序
4.插入排序
5.选择排序
6.及时终止的选择排序
7.归并排序
8.快速排序
9.基数排序
10.希尔排序

1.冒泡排序
主要思想:两重循环,遇到一个数比它后一个数大时就交换位置。
步骤:
1.比较相邻的元素。如果第一个比第二个大,就交换它们两个;
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
3. 针对所有的元素重复以上的步骤,除了最后一个;
4. 重复步骤1~3,直到循环完成。

//冒泡排序
template<class T>
void bubbleSort(T a[], int n) {
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (a[j] > a[j + 1]) {
                swap(a,j,j+1);
            }
        }
    }
}

时间复杂度O(n^2)
空间复杂度O(1)

2.及时终止的冒泡排序
添加判断代码,避免做无用功
主要思想:两重循环,遇到一个数比它后一个数大时就交换位置,直到每一个数的后一个数都比它本身大。只是当数组已经有序时便跳出循环
步骤:
1.比较相邻的元素。如果第一个比第二个大,就交换它们两个;
2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
3.针对所有的元素重复以上的步骤,除了最后一个;
4. 重复步骤1~3,直到排序完成。

//及时终止的冒泡排序
template<class T>
void bubbleSort2(T a[], int n) {
    bool swapped = true;//用于判断数组是否已经有序了
    for (int i = n; i > 1 && swapped; --i) {
        swapped = false;
        for (int j = 0; j < i - 1; ++j) {
            if (a[j] > a[j + 1]) {
                swap(a,j,j+1);
                swapped = true;
            }
        }
    }
}

时间复杂度O(n^2)
空间复杂度O(2)

3.计数排序
最快的但只能进行整数排序也不适合数值跨度范围很大的排序
主要思想:先计算每个数应该排在第几名,再通过数字的名次把它交换到它应处的位置。
步骤:
1.找出待排序的数组中最大和最小的元素;
2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
3.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。

//计数排序
template<class T>
void countSort(T a[], int n) {
	int bias, min = array[0], max = array[0];
	for (int i = 1; i < array.length; i++) {
            if (array[i] > max)
                max = array[i];
            if (array[i] < min)
                min = array[i];
        }
        bias = 0 - min;
    T *b = new T[max - min + 1];
    //先初始化数组b
    for (int i = 0; i < n; ++i) {
        b[i] = 0;
    }
    //计算名次
    for (int i = 0; i < array.length; i++) {
    	bucket[array[i] + bias]++;
    }
    //根据名次排序
    int index = 0, i = 0;
    while (index < array.length) {
    if (bucket[i] != 0) {
    	array[index] = i - bias;
        bucket[i]--;
        index++;
    } else
        i++;
    }
}

时间复杂度O(n+k)
k为输入的元素的最大值减最小值加1
空间复杂度O(k)

4.插入排序
主要思想:将每个数都插入到当前顺序中它应在的位置,当完成所有数的插入后,便成了有序数组。
步骤:
1.从第一个元素开始,该元素可以认为已经被排序;
2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
5.将新元素插入到该位置后;
6. 重复步骤2~5直到循环结束。

//插入排序
template<class T>
void insertSort(T a[], int n) {
    for (int i = 0; i < n; ++i) {
        T temp = a[i];
        int j;
        for (j = i - 1; j >= 0 && a[j] > temp; --j) {
            a[j + 1] = a[j];
        }
        a[j + 1] = temp;
    }
}

时间复杂度O(n^2)
空间复杂度O(1)

5.选择排序
主要思想:选择最大的数放到最后面,再选择除了已选出来的数中最大的,放到这些数都后面。
步骤:
1.选出数组中最大的数
2.将最大的数与数组最后一个数交换
3.数组长度减1
4.重复1-3直到循环结束

//选择排序
template<class T>
void selectionSort(T a[], int n) {
    for (int i = n; i > 1; --i) {
        int indexOfMax = 0;
        for (int j = 0; j < i; ++j) {
            if (a[indexOfMax] <= a[j]) {
                indexOfMax = j;
            }
        }
        swap(a,indexOfMax,n-1);
    }
}

时间复杂度O(n^2)
空间复杂度O(1)

6.及时终止的选择排序
主要思想:同上添加判断代码,避免做无用功
后面的数都>前面的数了则停止。
步骤:
1.选出数组中最大的数
2.将最大的数与数组最后一个数交换
3.数组长度减1
4.重复1-3直到排序完成

//及时终止的选择排序
template<class T>
void selectionSort2(T a[], int n) {
    bool sorted = false;//用于判断数组是否已经有序了
    for (int i = n; !sorted && i > 1; --i) {
        int indexOfMax = 0;
        sorted = true;
        for (int j = 0; j < i; ++j) {
            if (a[indexOfMax] <= a[j]) {
                indexOfMax = j;
            } else {
                sorted = false;
            }
        }
        swap(a,indexOfMax,n-1);
    }
}

时间复杂度O(n^2)
空间复杂度O(2)

7.归并排序
主要思想:两个两个的排序,当有序后就合并为一个单位元看待,再进行两个两个的排序,直到都合并成一个。
步骤:
1.把长度为n的输入序列分成两个长度为n/2的子序列;
2.对这两个子序列分别采用归并排序;
3.将两个排序好的子序列合并成一个最终的排序序列。
驱动程序

//归并排序
template<class T>
void mergeSort(T a[], int n) {
    T *b = new T[n];
    int segmentSize = 1;
    while (segmentSize < n) {
        mergePass(a, b, n, segmentSize);
        segmentSize += segmentSize;
        mergePass(b, a, n, segmentSize);
        segmentSize += segmentSize;
    }
    delete[] b;
}

确定需要归并的子序列的左右边界

template<class T>
void mergePass(T a[], T b[], int n, int segmentSize) {
    int i = 0;
    while (i <= n - 2 * segmentSize) {
        merge(a, b, i, i + segmentSize - 1, i + 2 * segmentSize - 1);
        i = i + 2 * segmentSize;
    }

    if (i + segmentSize < n) {
        merge(a, b, i, i + segmentSize - 1, n - 1);
    } else {
        for (int j = i; j < n; ++j) {
            b[j] = a[j];
        }
    }
}

实际完成基本的归并操作

template<class T>
void merge(T a[], T b[], int x, int y, int z) {
    int first = x, second = y + 1, result = x;
    while((first<=y)&&(second<=z)){
        if(a[first]<=a[second]){
            b[result++]=a[first++];
        } else{
            b[result++]=a[second++];
        }
    }
    if(first>y){
        for (int i = second; i <= z; ++i) {
            b[result++]=a[i];
        }
    } else{
        for (int i = first; i <= y; ++i) {
            b[result++]=a[i];
        }
    }
}

时间复杂度O(nlogn)
空间复杂度O(n)

8.快速排序
主要思想:先找一个数(这里找的是第一个),将小于它的数放到它左边,将大于它的数放到它右边,再将它左边和右边的数分别进行快速排序。
步骤:
1.从数列中挑出一个元素,称为 “基准”,这里取数组第一个元素;
2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区操作;
3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
驱动程序

//快速排序
template<class T>
void quickSort(T a[],int n){
    if(n<=1)
        return;
    int indexOfMax = 0;
    for (int j = 0; j < n; ++j) {
        if (a[indexOfMax] <= a[j]) {
            indexOfMax = j;
        }
    }
    swap(a[n-1],a[indexOfMax]);//将最大元素移到最右边
    quickSortStep(a,0,n-2);//进入递归程序
}

递归快速排序

template<class T>
void quickSortStep(T a[],int left,int right){
    if(left>=right)
        return;
    int lTemp=left+1,rTemp=right;
    T x=a[left];
    //将比支点小的元素换到从右侧换到左侧
    while (true){
        while (a[lTemp]<a[left]){
            lTemp++;
        }
        while (a[rTemp]>a[left]){
            rTemp--;
        }
        if(lTemp>=rTemp)
            break;
        swap(a,lTemp,rTemp);
    }
    swap(a[left],a[rTemp]);//将支点换到中间
    //在分别将左右侧元素进行快速排序
    quickSortStep(a,left,rTemp-1);
    quickSortStep(a,rTemp+1,right);
}

时间复杂度O(n^2)
空间复杂度O(logn)

9.基数排序
主要思想:先用最低位排序,再用下一位,一直排到最高位。
步骤:
1.取得数组中的最大数,并取得位数;
2.从最低位开始取每个位组成数组;
3.对每个由为元素组成的数组进行计数排序(利用计数排序适用于小范围数的特点);

//基数排序
template<class T>
void radixSort(T a[], int n) {
    //找出所有数中的最大位数
    int m = 0;
    for (int i = 0; i < n; ++i) {
        int k = 1;
        T temp = a[i];
        while (temp / 10 != 0) {
            temp /= 10;
            k++;
        }
        if (k > m) {
            m = k;
        }
    }
    //排序
    int num = 1;
    for (int j = 0; j < m; ++j) {//最大是几位就循环几次
        int x[10][n];//第j位属于0-9的哪一个就存入对应的x[0-9]
        int y[10];//存每个x[0-9]中有几个数
        for (int j = 0; j < 10; j++) {
            y[j] = 0;
        }//初始化
        int e, temp;
        for (int i = 0; i < n; i++) {
            temp = a[i] / num;
            e = temp % 10;
            x[e][y[e]] = a[i];
            y[e]++;
        }//存入
        int f = 0;
        for (int k = 0; k < 10; k++) {
            if (y[k] > 0)
                for (int i = 0; i < y[k]; i++) {
                    a[f] = x[k][i];
                    f++;
                }
        }//取出按顺序放回
        num *= 10;//进入高一位排序
    }
}

时间复杂度O(kn)
(k为最大数的位数)
空间复杂度O(n+k)

10.希尔排序
主要思想:又叫缩小增量排序,具体操作是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
步骤:
1.先将数组分为gap=length/2个序列
2.对每个序列进行插入排序
3.将gap缩小为gap/2,将数组分为gap个序列
4.重复2-3直到gap为0

void ShellSort(int[] array, int n) {
    int temp, gap = n / 2;
    while (gap > 0) {
	    for (int i = gap; i < len; i++) {
	    	temp = array[i];
	        int preIndex = i - gap;
	        while (preIndex >= 0 && array[preIndex] > temp) {
	        	array[preIndex + gap] = array[preIndex];
	            preIndex -= gap;
	        }
	        array[preIndex + gap] = temp;
	     }
     gap /= 2;
     }
}

时间复杂度为O(nlog2n)
空间复杂度O(1)

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值