今天我们的算法内容为插入排序和快速排序,难度比昨天上升了一点。
插入排序
思想:
将待排序的元素逐渐插入到已排序序列的合适位置中,有序序列不断扩大,直至全部。
将第一个元素视为已排序,然后从第二个元素开始往后遍历每个元素。
就如抓扑克牌一般,开局有一张牌,随后再摸一张,比第一张牌大放后面,比一张牌小放前面。
之后再摸一张牌,再与这原来的两张牌进行比较大小的,不断进行以上操作,最后牌摸完同时大小也拍好了。
事例:
当摸第i张牌时候,[0,i-1]的牌已经是排好顺序了的。
我们只需要将第i张牌与[0,i-1]里的牌进行大小比较,再进行插入即可,而其中[0,i-1]的牌是进行从后往前遍历的。
最后即可得到一个有序的牌库,也就是有序数组。
时间复杂度为O(n^2) 空间复杂度为O(1)
python代码如下:
n=int(input())#多少数字
a=list(map(int,input().split()))
for i in range(1,n):
number=a[i]
idx=0#插入位置
for j in range(i-1,-1,-1):#从后往前遍历已经分类好了数字。
if a[j]>number:
a[j+1]=a[j]#a[j+1]原来是number的位置因为number比a[j]小所以向前一位,而a[j+1]大小就成了a[j]
else: #也就是number>a[j]的情况。
idx=j+1 #这个时候也就确定好了插入位置,插入的位置就是j+1这个位置
break#前面的数字不用再判断,跳出循环
a[idx]=number
print(a)
除了用for循环写也可以用while来写。
n = int(input())
a = list(map(int, input().split()))
for i in range(1, n):
number = a[i]
idx = i - 1 定义插入位置
while idx >= 0 and a[idx] > number:#只有当两个条件都满足时候才进行while循环
a[idx + 1] = a[idx]
idx -= 1
a[idx + 1] = number#当while结束时idx+1的位置就是插入的位置
print(a)
c++代码如下:
#include <iostream>
using namespace std;
const int N = 1e3 + 1;
int a[N];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 1; i < n; i++) {
int number = a[i];//定义插入的数字
int j = i - 1;//插入位置
while (j >= 0 && a[j] > number) //当a[j]<number时候停止循环也就是number所需要插入的位置
{
a[j + 1] = a[j];
j--;//每小于一次j插入的位置-1
}
a[j + 1] = number;
}
for (int i = 0; i < n; i++) cout << a[i] << " ";
return 0;
}
你已经学会插入排序了,请你用插入排序试一下解决排序问题吧。
快速排序
思想:
快速排序之所以叫快速排序就是因为它比较快。
它最重要的思想是递归和分治。
还是用一堆扑克牌举例,与上次不一样的是,你没有边抽边排序,你是抽完以后再排序。
抽完以后你看着手中一堆的扑克牌,先选择一张牌,令它为基准值。
然后你将扑克牌分为三部分,一部分为大于这个牌的数字,另一部分是小于这个牌的数字,还有一部分就是它自己。
在之后小于它的牌,和大于它的牌再不断进行以上操作递归。
事例:
假设有个数组[4,1,5,7,9,8,3]
为了方便我们就设刚开始的基准值为4,基准值的下标为left,你也可以随便设为这个数组的某个数字。
刚开始存放小于等于基准值的数字的下标为idx=left+1。
开始遍历数组。
先遍历到1,因为1刚好是小于基准值的数字,所以之后比基准值小的数字要放在idx+1处。
5,7,9,8都大于4所以就放在原位置。
因为3小于四所以要进行换位,将3移动到idx的位置而idx上的数字移动到3的数字,随后idx+1再往后判断有没有数字小于基准值4进行换位。
最后我们产生了新的数组[4,1,3,5,7,9,8]。
而所有小于等于基准值的数字都放在[left+1,idx-1]处
随后我们再将基准值的数字与3进行换位,(为什么不是将小于基准值的数字插入前面呢,因为这样的时间复杂度太高了。)就是a[left],a[idx-1]=a[idx-1],a[left]。
最后就产生了新的数组[3,1,4,5,7,9,8]。
将大于基准值和小于基准值的数分开,而之后我们只需要进行不断的分治与递归即可。
python代码如下:
n = int(input())
a = list(map(int, input().split()))
def partition(a, left, right):
idx = left + 1 # 从left的下一个位置开始遍历
for i in range(left + 1, right + 1):
# 如果当前元素小于等于基准值,则交换
if a[i] <= a[left]:
a[idx], a[i] = a[i], a[idx]
idx += 1
# 将基准值放到中间(所有小于等于它的值的右边)
a[left], a[idx - 1] = a[idx - 1], a[left]
# 返回基准值的最终位置(也是左右子数组的分界点)
return idx - 1
def quicksort(a, left, right):
if left < right:
mid = partition(a, left, right) # 获取基准值的下标
quicksort(a, left, mid - 1) # 对基准值左边的子数组进行排序,递归
quicksort(a, mid + 1, right) # 对基准值右边的子数组进行排序,递归
quicksort(a, 0, n - 1)
print(*a)
c++算法如下
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 1;
int a[N];
int partition(int a[], int left, int right) {
int idx = left+1; // 从left+1的位置开始遍历
for (int i = left + 1; i <= right; i++) {
if (a[i] < a[left]) {
swap(a[idx], a[i]);
idx++;
}
}
swap(a[left], a[idx-1]);
return idx-1;//返回下标为idx-1
}
void quicksort(int a[], int left, int right) {
if (left < right) {
int mid = partition(a, left, right);//找出基准值的下标
quicksort(a, left, mid - 1);//左边的数进行以上的递归
quicksort(a, mid + 1, right);//右边的数进行以上的递归
}
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
quicksort(a, 0, n - 1);
for (int i = 0; i < n; i++) {
cout << a[i] << " ";
}
return 0;
}
学会了的可以去试一下这道题,你也可以用之前学的选择排序,插入排序试一下,你会发现不行。
OK今天的算法内容就到这了,今天是学算法的第二天,算法最重要的内容是要多练,你的放弃可能会成就更多的人,我们明天再见。