数据结构学习之——排序进阶(快速排序)

排序

冒泡排序:
一种比较容易理解的排序算法,可以优化的程度也比较有限
平均:O(n^2)
最坏:O(n^2)
最好:O(n)
最好情况需要在进行循环之前加入标志位,若一次遍历中,没有任何两个数据被交换,则认为序列已经有序,跳出循环。此时,时间复杂度为O(n)
插入排序:
选择一个数据插入到前面已经排好的有序队列之中
平均:O(n^2)
最坏:O(n^2)
最好:O(n)
这两种算法中规中矩,便于理解但是对于较大规模的数据排序而言,显得有些笨重,所以学习一下比较高级的排序算法。

快速排序(基本思路)

快速排序的基本思想是,选择随机数的同时将大于该元素的所有其他元素需要放到后面,将小于该元素的所有其他元素放到前面,然后将该元素放中间位置,该位置即为最后该元素确定的位置。
举个栗子:这是一个待排序的序列

   3  4  2  7  8  6  1  9  5

此时,假设我们随机选择了数字6
那么调整后的数据为

   3  4  2  1  5  X  7  9  8

此时我们可以很清楚的看到,在将大于6和小于6的元素放到两边后,此时中间空余的X位置即为6需要插入的位置。(如果我随机取得数据和比较的数据相等怎么办?即队伍中有相同元素我应该将它放到左边还是右边?这个问题的答案将在程序设计的过程中给出)
然后我们递归的处理左右两边的数组,即可最终得到排序后的数组。

程序设计(递归)

3 4 2 7 8 6 1 9 5 还是以这个数列举栗
进入函数时,应当是这样的情况

   3  4  2  7  8  6  1  9  5
   ↑                       ↑
  left                   right

此时我们随机取得数据(取中间数据)
之所以选择取中间数据是因为,假设我们原有数据即为有序,我们每次都取得的数据在开头,那最后插入的位置还是在开头,最后得到的两列数据并没有得到合理的分割,此时,运行的时间复杂度仍为O(N^2),这种结果是很可惜的,所以尽量在中间取值,使得数据可以尽量随机。

   3  4  2  7  -  6  1  9  5       			          8
   ↑           ↑           ↑                          ↑
  left        mid        right                        X

此时我们取得数据8,原先所在的位置mid已经为空
此时我们现在left所在的数据换到mid处,空出left

   -  4  2  7  3  6  1  9  5       			          8
   ↑           ↑           ↑                          ↑
  left        mid        right                        X

然后进入循环:
right所在的数据与随机取得X比较
如果大于X,则right向左移动一格,即right- -
反之right所在的数据放到left处,left++

   5  4  2  7  3  6  1  9  -       			          8
      ↑                    ↑                          ↑
     left                right                        X

若发生数据交换,则开始从left比较
如果小于X,则left向右移动一格,即left++
反之left所在的数据放到right处,right–

   5  4  2  7  3  6  1  9  -       			          8
         ↑                 ↑                          ↑
        left             right                        X

   5  4  2  7  3  6  1  9  -       			          8
            ↑              ↑                          ↑
           left          right                        X
	。
	。
	。         
   5  4  2  7  3  6  1  9  -       			          8
                        ↑  ↑                          ↑
                     left  right                      X
                     
   5  4  2  7  3  6  1  -  9       			          8
                        ↑                             ↑
                      left                            X
                      right

此时left==right,退出循环,然后插入X,

	5  4  2  7  3  6  1  8  9      
						 ↑
	                     X

递归的对X左边部分和右边部分进行上述操作

申明函数,需要三个参数
1.数组 :待排序的数组
2.left :需要排序的左边界
3.right:需要排序的有边界
递归(int * 数组,int left,int right){
	保存left
	保存right
	if(数组长度小于等于1){      //设置递归结束条件
		break;
	}
	随机取得元素X
	while(1){                  //循环
		if(left==right){       //循环结束判断   
			break;
		}
		//在左边寻找大于X的数放到右边
		if(falg==1){           
			if(left所在的数据大于X){
				将该数据放到right所在位置
				right--
				falg=0
			}
			else{
				left++
			}
		}
		//在右边寻找小于X的数放到左边
		else{
			省略了,代码里会给出
		}
	}
	递归(递归(int * 数组,左边部分left,左边部分right)
	递归(递归(int * 数组,右边部分left,右边部分right)
}

程序

//快速排序,封装,方便使用
int Quick_Sort(int *list, int n) {
	int left, right;
	if (n == 0 || n == 1) {
		return 1;
	}
	left = 0;
	right = n - 1;
	QS(list, left, right);
	return 1;
}
//快速排序递归
int QS(int *list, int left, int right) {
	int L, R;
	int mid;
	int data,flag=1;
	//保存左右边界
	L = left;
	R = right;
	if (left >= right) {
		return 1;
	}
	//取比较元素
	mid = (left + right) / 2;
	data = list[mid];
	list[mid] = list[right];
	while (1) {
		//遍历结束后跳出循环,左右边界标志位相遇
		if (left == right) {
			list[left] = data;
			break;
		}
		//从左边开始找大于比较元素的值插入到右标志位所在的地方
		if (flag == 1) {
			if (list[left] >= data) {
				list[right] = list[left];
				flag = 0;
				right--;
			}
			else {
				left++;
			}
		}
		//从右边开始找小于比较元素的值插入到左标志位所在的地方
		else {
			if (list[right] <= data) {
				list[left] = list[right];
				flag = 1;
				left++;
			}
			else {
				right--;
			}
		}
	}
	//递归计算左边部分
	QS(list, L, right-1);
	//递归计算右边部分
	QS(list, right + 1, R);

	return 1;
}

测试(对100000个随机数进行排序)

int main() {
	int i, n = 100000;
	int list[100000];

	clock_t start, finish;
	double  duration;

	for (i = 0; i < n; i++) {
		list[i] = rand();
	}

	start = clock();
	//Bubble_Sort(&list[0], n);
	//qsort(&list[0], 100000, sizeof(int), comp);
	Quick_Sort(list, n);
	finish = clock();
	duration = (double)(finish - start) / CLOCKS_PER_SEC;
	printf("%f seconds\n", duration);

	

	printf("The end ..");
	while (1);
	return 0;
}

在这里插入图片描述
速度出乎意料的快,100000数据排序时间仅为0.01s,比起常规排序20+s的时间而言简直是飞跃。
细心的可能看到我在测试函数还测试了冒泡排序和stdlib中内置的快速排序进行了比较,看看结果吧!
在这里插入图片描述
简单易懂的冒泡排序表现确实令人失望。。。
再来看看官方内置函数
在这里插入图片描述
有点出乎意料,官方内置的函数运行时间略差与我自己写的快排算法,大概是有些细节,官方函数考虑的更加周全浪费了一些时间?看了下源码基本上不可读,原因要未来有空好好研究了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值