八大排序算法1(冒泡 选择 插入 希尔 桶排序)

八大排序算法

  • 初级排序
    • 冒泡排序
      • 两种实现
      • 冒泡排序的优化算法
    • 选择排序
      • 编程要点
      • 代码实现
    • 插入排序
      • 编程要点
      • 代码实现
  • 进阶排序
    • 希尔排序
    • 基数(桶)排序
    • 堆排序
    • 归并排序
    • 快速排序
      • 递归实现
        • 算法要点
        • 代码实现
      • 非递归实现
        • 编程要点
        • 代码实现

初级排序

冒泡排序

冒泡排序,从前往后就是把最大的冒泡到最后面,缩小区间,或者从最后面开始,每次把最小的冒泡到最前面

两种实现

打印数组的值

#include
#include
void PrintAr(int* ar, int n)
{
    assert(ar != nullptr);
    for (int i = 0; i < n; ++i)
    {
        printf("%5d", ar[i]);
    }
    printf("\n");
}

交换函数swap

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

这个是从后向前遍历,每次把最小的放到最前面
注意越界条件,j的取值,j+1 时候是否会越界

void bubblesort1(int* ar, int n) {
    assert(ar != nullptr);
    if (n <= 1)return;
    int i, j;
    for (int i = n - 1; i >= 0; i--) {
        for (int j = n - 2; j >= n - i - 1; j--) {
            if (ar[j] > ar[j + 1]) {
                swap(&ar[j], &ar[j + 1]);
            }
        }
    }
}

这个是从前往后遍历,每次把最大的放到最后面
注意越界条件,j的取值,j+1 时候会越界

void bubblesort2(int* ar, int n) {
    assert(ar != nullptr);
    if (n <= 1)return;
    int i, j;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n - i-1; j++) {
            if (ar[j] > ar[j + 1]) {
                swap(&ar[j], &ar[j + 1]);
            }
        }
    }

}

冒泡排序的优化算法

这个是从后向前遍历,每次把最小的放到最前面,注意越界条件,j的取值,j+1 时候是否会越界,假如排了几次,发现,已经有序了,就不用再继续排了,直接跳出就行

void bubblesort1plus(int* ar, int n) {
    assert(ar != nullptr);
    if (n <= 1)return;
    int i, j;
   
    for (int i = n - 1; i >= 0; i--) {
        bool flag = false;
        for (int j = n - 2; j >= n - i - 1; j--) {
            if (ar[j] > ar[j + 1]) {
                swap(&ar[j], &ar[j + 1]);
                flag = true;
            }
        }
        if (flag == false)break;
    }
}

选择排序

选择排序,就是每次在指定的区间选择最小的元素,放到最前面

编程要点

先判断指针是否为空,然后判断如果n<=1 则说明一个数据或者没有数据
则不用排序,直接返回,
外层循环只用从0开始遍历到n-2位置,因为最后一个数不用比较,一定是最大的,
如果i本身就是minpos,则不用交换

代码实现

void SelectSort(int* ar, int n)
{
    assert(ar != nullptr);
    if (n <= 1) return;
    for (int i = 0; i < n - 1; ++i)
    {
        int minpos = i;
        for (int j = i + 1; j < n; ++j)
        {
            if (ar[minpos] > ar[j])
            {
                minpos = j;
            }
        }
        if (i != minpos)
        {
            Swap(&ar[minpos], &ar[i]);
        }
    }
}

插入排序

插入排序,就是遍历每一个数据,将其插入到合适的位置,类似最大堆搜索函数的实现

编程要点

先判断指针是否为空,然后判断如果n<=1 则说明一个数据或者没有数据
//则不用排序,直接返回
//从第二个数据开始,看是否需要向前插入,
//如果大圩前面的数,则直接遍历到第三个数据
//如果小于前面一个数,则需要向前插入,
//插入步骤是,现将该数提取到temp,前面的数下标为j,将前面的数移动到该位置,即ar[j+1]=ar[j] 然后j–;
// 看temp是否能插入到前面的一个位置,如果该temp大于签的的数
// 则可以插入到j+1位置,否则,继续将j位置到j+1位置
//循环的结束条件是temp>ar[j] 和j==-1 则将temp放在j+1位置

代码实现

void InsertSort(int* ar, int n)
{
    assert(ar != nullptr);
    if (n <= 1) return;
    for (int i = 1; i < n; ++i)
    {
        PrintAr(ar, n);
        if (ar[i - 1] > ar[i])
        {
            int j = i - 1;
            int tmp = ar[j + 1];
            do
            {
                ar[j + 1] = ar[j];
                j = j - 1;
            } while (j >= 0 && ar[j] > tmp);
            ar[j + 1] = tmp;
        }
    }
}

进阶排序

希尔排序

基数(桶)排序

堆排序

堆排序的思想就是先将待排序的序列建成大根堆,使得每个父节点的元素大于等于它的子节点。此时整个序列最大值即为堆顶元素,我们将其与末尾元素交换,使末尾元素为最大值,然后再调整堆顶元素使得剩下的 n−1 个元素仍为大根堆,再重复执行以上操作我们即能得到一个有序的序列

归并排序

归并排序利用了分治的思想来对序列进行排序。对一个长为 n 的待排序的序列,我们将其分解成两个长度为n/2的子序列。每次先递归调用函数使两个子序列有序,然后我们再线性合并两个有序的子序列使整个序列有序

void PrintAr(int* ar, int n)
{
	assert(ar != nullptr);
	for (int i = 0; i < n; ++i)
	{
		printf("%5d", ar[i]);
	}
	printf("\n");
}
//类似将两个有序的数组合并成一个有序的数组,
//一个数组,pos左边的有序,pos右边的也有序
void MergeAr(int* dest, int* src, int left, int pos, int right) {
	assert(dest != nullptr && src != nullptr);
	int i = left, j = pos + 1;
	int k = left;
	while (i <= pos && j <= right) {
		dest[k++] = src[i] < src[j] ? src[i++] : src[j++];
		
	}
	while (i <= pos) {
		dest[k++] = src[i++];
	}
	while (j<= right) {
		dest[k++] = src[j++];
	}
}
void CopyAr(int* dest, int* src, int left, int right) {
	assert(dest != nullptr && src != nullptr);
	for (int i = left; i <= right; i++) {
		dest[i] = src[i];
	}
}

//int m = left + (right - left) / 2;  不能是left + (right - left+1) / 2;
//每次到(0,1)  passsort(ar, br, 0, 1);  会陷入死递归,出不来栈溢出
//55  45  33  22  11  0
//0     1    2    3   4    5
// 
//m=2
//55  45  33    |         22  11  0
//0     1    2     |         3   4    5
//m=1
//55  45  |  33    |         22  11  0
//0     1   |   2     |         3   4    5
//m=0

//55 | 45  |  33    |         22  11  0
//0   |  1   |   2     |         3   4    5

//合并  使其有序
//55 | 45 
//0   |  1 
//45 | 55 
//0   |  1 

//下一次合并
//45 | 55  |  33   
//0   |  1   |   2   
//33 | 45  |  55   
//0   |  1   |   2   

//if (left < right) 不要写成  while(left

//递归到只有一个数据的时候,退出,合并这两个数据,每一个数据自己是有序的,‘
//所以每次递归的底部是,一个数据,即left==right
void passsort(int* br, int* ar, int left, int right) {
	assert(ar != nullptr && br != nullptr);
	if (left < right) {
		int m = left + (right - left) / 2;
		passsort(br, ar, left, m);
		passsort(br, ar, m + 1, right);
		MergeAr(br, ar, left, m, right);
		CopyAr(ar, br, left, right);
		PrintAr(ar, 10);
	}
}
//归并排序
//开辟br存储归并后的ar
void mergesort(int* ar, int n) {
	assert (ar != nullptr);
	if (n <= 1)return;
	int* br = (int*)malloc(sizeof(int) * n);
	if (br == nullptr)exit(EXIT_FAILURE);
	passsort(br, ar, 0, n - 1);
	free(br);
	br = nullptr;
}
int main()
{
	int ar[] = { 78,12,23,90,100,34,89,45,56,67 };
	int n = sizeof(ar) / sizeof(ar[0]);
	PrintAr(ar, n);
	mergesort(ar, n);
	PrintAr(ar, n);
	return 0;
}

快速排序

快速排序的主要思想是通过划分将待排序的序列分成前后两部分,其中前一部分的数据都比后一部分的数据要小,然后再递归调用函数对两部分的序列分别进行快速排序,以此使整个序列达到有序

递归实现

核心是分治算法的体现

算法要点

partitiaon函数算法要点
//将ar数组的数分成两部分,左边的部分是都是<=ar[i],右边的都是>ar[i]
//大致思路是,在右边开始遍历,找比temp小的数,找到后直接赋值给i下标
// 从左边遍历,找比temp大的数,找到后直接赋值给j下标
// while (itemp)j–;中加条件i //j–后,i=j,就不用比较了,加入条件,if(i //while (i < j && ar[i] <= temp)i++;,如果不加i //导致i>j 就会出错

int Parition(int* ar, int left, int right) {
    assert(ar != nullptr);
    int i = left, j = right;
    int temp = ar[i];
    while (i < j) {
        while (i<j && ar[j]>temp)j--;
        if(i<j)ar[i] = ar[j];
        while (i < j && ar[i] <= temp)i++;
        if(i<j)ar[j] = ar[i];
    }
    ar[i] = temp;
    return i;
}

//不断地缩小区间,每次看pos,pos左边都小于等于ar[pos]
//右边都大于ar[pos] 就算是两个数,也要排序,所以,当left==right
//说明这一边只有一个数了,就不用再递归排序了

代码实现

void Qsort(int* ar, int left, int right) {
    assert(ar != nullptr);
    if (left < right) {
        int pos = Parition(ar, left, right);
        Qsort(ar, left, pos - 1);
        Qsort(ar, pos + 1, right);
    }   
}
void quicksort1(int* ar, int n) {
    assert(ar != nullptr);
    Qsort(ar, 0, n - 1);
}

非递归实现

编程要点

//非递归的排序算法 快排 需要用到队列,即链式队列,先进先出
//队列为空时说明排序完成,退出
//队列不为空时,就出队,将其当做一个区间,算出中间的pos,然后再将两个区间入队
//如果区间为pos + 1 < right) 即右边只有一个数据,就不要继续排序,不用入队,//pos - 1 > left) pos位置是,它左边都小于等于他自己,如果pos - 1 = left,左边只有一个数,不用入队

代码实现

void quicksort2(int* ar, int n) {
    assert(ar != nullptr);
    if (n <= 1)return;
    LinkQueue myQ;
    InitQueue(&myQ);
    Push(&myQ, 0);
    Push(&myQ, n - 1);
    int left, right;
    while (1) {
        
        if (IsEmpty(&myQ))break;
        Pop(&myQ, &left);
        if (IsEmpty(&myQ))break;
        Pop(&myQ, &right);
        int pos = Parition(ar, left, right);
        if (pos - 1 > left) {
            Push(&myQ, left);
            Push(&myQ, pos);
        }
        if (pos + 1 < right) {
            Push(&myQ, pos + 1);
            Push(&myQ, right);
       }
    }
    DestroyQueue(&myQ);

}

你可能感兴趣的:(算法,排序算法,算法)