思想
冒泡排序,就是两个两个元素进行比较,将较大的向后交换
第一趟排序将最大值放在最后边
每一趟排序都将一个元素放到最终位置
特性
Code
/*最基本冒泡排序*/
void BubbleSort(int R[] , int n){
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
for(i=0;i<n;++i){
for(j=0;j<n-1;++j){
if(R[j] > R[j+1]){ // 两两比较
temp = R[j];
R[j] = R[j+1];
R[j+1] = temp;
}
}
}
}
/*提高效率冒泡排序*/
void BubbleSort(int R[] , int n){
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
for(i=0;i<n-1;++i){ // n-1 趟所有元素都有序
for(j=0;j<n-1-i;++j){ // -i 因为每趟最后一个都确定,所以就减少一次比较
if(R[j] > R[j+1]){ // 两两比较
temp = R[j];
R[j] = R[j+1];
R[j+1] = temp;
}
}
}
}
/*提高效率冒泡排序、初始值不一样*/
void BubbleSort(int R[] , int n){
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
for(i=1;i<=n-1;++i){ // n-1 趟所有元素都有序
for(j=0;j<n-i;++j){ // -i 因为每趟最后一个都确定,所以就减少一次比较
if(R[j] > R[j+1]){ // 两两比较
temp = R[j];
R[j] = R[j+1];
R[j+1] = temp;
}
}
}
}
/*提高效率的冒泡排序、初始值不一样*/
void BubbleSort(int R[] , int n){
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
for(i=n-1;i>=1;--i){ // n-1 趟所有元素都有序
for(j=0;j<i;++j){ // -i 因为每趟最后一个都确定,所以就减少一次比较
if(R[j] > R[j+1]){ // 两两比较
temp = R[j];
R[j] = R[j+1];
R[j+1] = temp;
}
}
}
}
/*最高效率的冒泡排序*/
void BubbleSort(int R[] , int n){
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
int flag; // flag 标志着这一趟有无元素交换
for(i=0;i<n-1;++i){ // n-1 趟所有元素都有序
flag = 0; // 0 表示元素未发生交换,每趟排序前置0
for(j=0;j<n-1-i;++j){ // -i 因为每趟最后一个都确定,所以就减少一次比较
if(R[j] > R[j+1]){ // 两两比较
temp = R[j];
R[j] = R[j+1];
R[j+1] = temp;
flag = 1; // 1 表示元素发生交换,每当交换就置为1
}
}
if(flag == 0){
return; // 元素未交换,标明序列已经有序,直接结束程序
}
}
}
注:flag 可以套用上述任何一个 冒泡排序中
思想
简单选择排序,每一趟从序列中挑出一个最小的放到序列前端,循环下去元素便有序
特性
Code
void selectSort(int R[] , int n){
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
int min; // 记录最小值下标
for(i=0;i<n;++i){
min = i;
for(j=i+1;j<n;++j){ // i+1,开始,因为每趟 i 位置当作最小的
if(R[j] < R[min]){
min = j; // 记录更小值下标
}
}
if(min != i){ // 位置不同,元素交换
temp = R[min];
R[min] = R[i];
R[i] = temp;
}
}
}
思想
直接插入排序
将序列分成两部分,一部分是已排序序列,另一部分是待排序序列
每次拿待排序序列的第一个元素,和已排序序列从后向前的每个元素比较,找到适当插入位置
循环过程中,将元素不停后移,方便腾出 插入位置
特性
Code
/*方法一*/
void InsertSort(int R[] , int n){
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
for(i=1;i<n;++i){ // 0 位置是已排序序列,i位置是待排序序列第一个元素
temp = R[i];
for(j=i-1;j>=0&&R[j]>temp;--j){ // 从后向前比较
R[j+1] = R[j]; // 元素后移
}
R[j+1] = temp;
}
}
/*方法二*/
void InsertSort(int R[] , int n){
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
for(i=1;i<n;++i){ // 0 位置是已排序序列,i位置是待排序序列第一个元素
temp = R[i];
j=i-1;
while(j>=0&&R[j]>temp){
R[j+1] = R[j];
--j;
}
R[j+1] = temp;
}
}
思想
希尔排序(缩小增量排序)
其实就是直接插入排序的特殊版
将数据进行分组编号,然后拿每一组相同编号的数据,进行直接插入排序,待整个增量排完,序列就有序
增量的选取规则
1)希尔
|_n/2_| 、|_n/4_|、...、|_n/2的k次方_|、..、2、1
2)帕佩尔诺夫和斯塔舍维奇
2的(k+1)、...、65、33、17、9、5、3、1
3)增量选取注意
增量序列的最后一个值一定取 1
增量序列中的值应尽量没有除 1 之外的公因子
特性
Code
/*方法一*/
void shellSort(int R[] , int n , int k){ // k 增量因子
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
for(i=k;i<n;++i){ // i是从第二组第一个元素开始遍历(待排序序列)
temp = R[i];
for(j=i-k;j>=0&&R[j]>temp;j -= k){ // j每次变化是 k
R[j+k] = R[j];
}
R[j+k] = temp;
}
}
/*方法二*/
void shellSort(int R[] , int n , int k){ // k 增量因子
int i,j,temp; // i: 外层循环变量 j: 内层循环变量 temp: 临时交换变量
for(i=k;i<n;++i){ // i是从第二组第一个元素开始遍历(待排序序列)
temp = R[i];
j = i-k;
while(j>=0&&R[j]>temp){
R[j+k] = R[j];
j -= k; // j每次变化是 k
}
R[j+k] = temp;
}
}
思想
快速排序
i 标记第一个元素,j 标记最后一个元素,t 是基准值
首先将 i 位置元素给 基准值
其次,j从后向前去找比基准值小的元素,找到后将这个元素放到 i 位置,结束当前查找
再次,i从前向后去找比基准值大的元素,找到后将这个元素放到 j 位置,结束当前查找
最后,当 i 和 j 相等时,基准值元素归位,放到这个位置上,将序列一分为二。
递归对左右序列执行上述过程,最终元素会有序
快排无序速度快,有序速度慢
特性
Code
void QuickSort(int R[] , int low , int high){
int i = low , j = high; // i,j 替换
int t; // 基准值
if(i < j){
t = R[i]; // 将第一个元素做基准值
while(i < j){ // i = j 结束循环,基准值归位
while(i<j && t<=R[j])
--j;
if(i<j){
R[i] = R[j];
++i;
}
while(i<j && t > R[i])
++i;
if(i<j){
R[j] = R[i];
--j;
}
}
R[i] = t; // 基准值归位
QuickSort(R , low , i-1);
QuickSort(R , i+1 , high);
}
}
有一种简单的排序算法叫作计数排序。这种算法对一个待排序表(用数组A[] 表示)进行排序,排序结果存储在另一个新的表中(用数组 B[]表示),表中关键字为 int 型。必须注意的是,表中所有待排序的关键字互不相同,计数排序算法针对表中的每个关键字,扫描待排序表一趟,统计表中有多少个关键字比该关键字小。假设对某一个关键字,统计出数值为 c ,那么这个关键字在新的有序表中的位置即为 c 。
分析:
1)只需要双重循环,每次都从头开始扫描 A 数组中的数据即可
2)在最内层循环,进行比较,看外层的数值 是否大于内层的数据,大就加加
3)内层循环结束后,在这里将 B[c] = A[i];
void countSort(int A[] , int B , int n){
int i,j,c;
for(i=0;i<n;++i){
c = 0; // 这样每次 c 都从新开始
for(j=0;j<n;++j){
if(A[i] > A[j]){
++c;
}
}
B[c] = A[i];
}
}
设计一个双向冒泡排序算法,即在排序过程中交替改变扫描方向。
分析:
1)从左向右扫,把大的向后放
2)从右向左扫,把小的向前放
void twoBubbleSort(int A[] , int n){
int left,right,i,j;
left = 0;
right = n-1;
bool flag = true;
while(true){
flag = false;
for(i=0;i<right;++i){
if(A[i] > A[i+1]){
swap(A[i],A[i+1]); // swap 已定义交换函数
flag = true;
}
}
--right;
for(j=right;j>left;--j){
if(A[j] < A[j-1]){
swap(A[j],A[j-1]);
flag = true;
}
}
++left;
}
}
flag 是标着这 排序的时候元素时候发生过 交换,如果没有交换,就代表着元素已经有序,无序再排
给定一组关键字,创建一个带头结点的链表,设计一个直接插入排序算法,对这个单链表进行递增排序。
分析:
1)首先我们回顾下,直接插入排序算法的思想:分成待排序和已排序区域,每次拿待排序区域的第一个元素和已排序从后向前每一个元素进行比较,待排序区域元素小,已排序区域元素就后移,直到找到合适的位置
2)因此,我们可以将链表分成两个区域,一个待排序区域和一个已排序区域,即链表首元结点是已排序,其后的是待排序区域,然后进行依次比较,好处是我们不需要元素后移。
void create(LNode *&L , int A[] , int n){
L = (LNode *)malloc(sizeof(LNode));
L->next = NULL;
int i;
LNode *r,*p;
r = L;
for(i=0;i<n;++i){
p = (LNode *)malloc(sizeof(LNode));
p->next = NULL;
p->data = A[i];
r->next = p;
r = p;
}
}
void sort(LNode *L){
LNode *p,*q,*t;
p = L; // p 指向头结点,从首元结点向后走
q = p->next; // q 指向待排序的第一元素
while(q != NULL){
t = q->next; // t 指向 q 的下一个结点
if(p->next->data < q->data && p->next != q){ // 待排序元素值 小于 已排序元素值
p = p->next;
}
if(p->next != q){ // 如果在已排序中找到位置,就插入,否则就不用动
q->next = p->next;
p->next = q;
}
q = t;
p = L; // L 重新开始
}
}