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

排序

冒泡排序:
一种比较容易理解的排序算法,可以优化的程度也比较有限
平均: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
    评论
### 回答1: Python计算思维训练——数组之线性代数是一门关于使用Python语言行线性代数计算的课程。该课程主要涵盖了向量、矩阵、线性方程组、矩阵分解、特征值与特征向量等内容。通过学习该课程,可以提高学生的线性代数计算能力,为日后从事相关领域的工作打下坚实的基础。 ### 回答2: Python计算思维训练——数组之线性代数是一门针对处理数学中线性代数问题的课程。这门课程主要涉及到了向量、矩阵、矩阵运算、矩阵特征值等方面的概念和运算,同时也介绍了如何将这些概念以Python编程语言的形式来描述和实现解决问题。 在Python计算思维训练中,学习线性代数可以帮助我们更好地掌握有关数据分析和机器学习的相关知识,因为这些领域的很多概念和运算都与线性代数息息相关。线性代数中的向量、矩阵、特征值等概念可以帮助我们更好地理解和解决数据的问题。 在Python中,我们可以使用NumPy来实现线性代数中的矩阵运算,NumPy是Python中一个常用的数学计算库,它提供了大量的数学函数和运算符,可以帮助我们更高效地解决线性代数中的问题。 例如,我们可以利用NumPy中的dot函数来计算矩阵的乘积,使用linalg函数可以计算矩阵的特征值和特征向量。对于向量的范数,我们可以使用linalg.norm函数。 此外,在Python中还有一些有用的库,如SciPy和SymPy,这些库提供了更高级和更复杂的数学函数和运算,可以使我们更加轻松地解决涉及高级数学问题的任务。 总之,Python计算思维训练——数组之线性代数,让我们了解和掌握了处理线性代数问题的基本概念和运算,这些知识对于我们解决数据的问题是非常有帮助的,同时也为我们理解和使用各种机器学习算法提供了重要的基础。 ### 回答3: Python 的计算思维训练中,数组之线性代数是必不可少的一部分。线性代数是一门数学分支,旨在研究线性方程组、向量空间以及线性变换等内容。在 Python 中,NumPy 库便提供了很多用于实现线性代数操作的函数,使得 Python 也成为了行线性代数计算的重要工具之一。 在 Python 中实现线性代数计算的关键是要了解如何使用 NumPy 数组来表示向量和矩阵。在 NumPy 中,可以使用一维数组来表示向量,使用二维数组来表示矩阵。同时,NumPy 还提供了很多用于实现线性代数计算的函数,比如矩阵加法、矩阵乘法、矩阵转置等。 通过使用 NumPy 中的函数,实现线性代数计算变得非常方便。例如,要对两个矩阵行加法,可以使用 np.add() 函数来实现,如下所示: import numpy as np a = np.array([[1, 2], [3, 4]]) b = np.array([[5, 6], [7, 8]]) c = np.add(a, b) print(c) 输出结果为: [[ 6 8] [10 12]] 同样的,要对两个矩阵行乘法,可以使用 np.dot() 函数来实现,如下所示: a = np.array([[1, 2], [3, 4]]) b = np.array([[5, 6], [7, 8]]) c = np.dot(a, b) print(c) 输出结果为: [[19 22] [43 50]] 可以看到,使用 NumPy 提供的函数,实现矩阵的加法和乘法变得非常简单。 除了基本的矩阵运算外,NumPy 还提供了很多用于实现更高级的线性代数计算的函数,比如矩阵求逆、矩阵行列式、特征值和特征向量等。这些函数可以帮助我们更方便的解决复杂的线性代数问题。 总之,Python 的计算思维训练中,数组之线性代数是非常重要的一部分。只有掌握了 NumPy 数组的基本操作和线性代数相关的函数,才能更好地发挥 Python 程序在实现复杂线性代数计算方面的优势。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值