常见的排序搜索算法
冒泡排序
对未排序的各元素从头到尾依次比较相邻的元素大小关系。
例如我们要把一堆数据从小到大进行排列
从第一个元素开始,把第一个元素和其右边的元素进行比较,如果第一个元素大,就将其和其右边的元素交换位置。下面将交换后的元素继续与其右边的元素进行比较,直到把最大的元素排到最右端,第一轮排序就结束了。
按照这个思路继续从最左端开始,这次走到倒数第二个位置即可。
依次类推,就可以将数据排序完成。
图解:
[3,7,2,6,9,1,4]
第一轮排序
3 | 7 | 2 | 6 | 9 | 1 | 4 |
---|---|---|---|---|---|---|
3 | 7 | 2 | 6 | 9 | 1 | 4 |
3 | 2 | 7 | 6 | 9 | 1 | 4 |
3 | 2 | 6 | 7 | 9 | 1 | 4 |
3 | 2 | 6 | 7 | 9 | 1 | 4 |
3 | 2 | 6 | 7 | 1 | 9 | 4 |
3 | 2 | 6 | 7 | 1 | 4 | 9 |
第二轮排序
3 | 2 | 6 | 7 | 1 | 4 | 9 |
---|---|---|---|---|---|---|
2 | 3 | 6 | 7 | 1 | 4 | 9 |
2 | 3 | 6 | 7 | 1 | 4 | 9 |
2 | 3 | 6 | 7 | 1 | 4 | 9 |
2 | 3 | 6 | 1 | 7 | 4 | 9 |
2 | 3 | 6 | 1 | 4 | 7 | 9 |
第三轮排序
2 | 3 | 6 | 1 | 4 | 7 | 9 |
---|---|---|---|---|---|---|
2 | 3 | 6 | 1 | 4 | 7 | 9 |
2 | 3 | 6 | 1 | 4 | 7 | 9 |
2 | 3 | 1 | 6 | 4 | 7 | 9 |
2 | 3 | 1 | 4 | 6 | 7 | 9 |
第四轮排序
2 | 3 | 1 | 4 | 6 | 7 | 9 |
---|---|---|---|---|---|---|
2 | 3 | 1 | 4 | 6 | 7 | 9 |
2 | 1 | 3 | 4 | 6 | 7 | 9 |
第五轮排序
2 | 1 | 3 | 4 | 6 | 7 | 9 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 6 | 7 | 9 |
冒泡排序的实现
list = [3, 7, 2, 6, 9, 1, 4]
for i in range(len(list) - 1):
for j in range(len(list) - 1 - i):
if list[j] > list[j + 1]:
list[j], list[j + 1] = list[j + 1], list[j]
print(list)
选择排序
选定第一个索引的位置,然后和后面的元素依次比较
如果后面的元素,小于第一个索引位置的元素,则交换位置。
经过一轮的比较后,可以确定第一个元素的位置是最小(或者最大的)
然后用同样的方法把剩余的元素逐个比较即可
代码设计分析(从小到大排序):
- 选择排序第一次将第0位置的数据取出, 和后面的数据(1, 2, 3…)依次比较, 如果后面的数据更小, 那么就交换.
- 这样经过一轮之后, 第一个肯定是最小的数据.
- 第二次将第1位置的数据取出, 和后面的数据(2, 3, 4…)依次比较, 如果后面的数据更小, 那么就交换.
- 这样经过第二轮后, 第二个肯定是次小的数据.
- 第三轮…第四轮…直到最后就可以排好序了. 有发现规律吗?
- 外层循环依次取出0-1-2…N-2位置的数据作为index(N-1不需要取了, 因为只剩它一个了肯定是排好序的)
- 内层循环从index+1开始比较, 直到最后一个.
list = [3, 7, 2, 6, 9, 1, 4]
for i in range(len(list)):
min = i
for j in range(i + 1, len(list)):
if list[min] > list[j]:
min = j
list[i], list[min] = list[min], list[i]
print(list)
插入排序
- 插入排序思想的核心是局部有序. 什么是局部有序呢?
- 比如在一个队列中的人, 我们选择其中一个作为标记的队员. 这个被标记的队员左边的所有队员已经是局部有序的.
- 这意味着, 有一部分人是按顺序排列好的. 有一部分还没有顺序.
插入排序的思路:
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果该元素(已排序)大于新元素,将该元素移到下一位置
- 重复上一个步骤,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入到该位置后, 重复上面的步骤.
- 总结:假设一个空教室,里面按照从低到高的身高站座位,教室外面的人进入一个就跟最高的比较 比它高的人就往后移动一个位置 直到找到最后一个移动位置的人 把移动后的位置给新人
代码设计分析:
- 插入排序应该从下标值1开始(因为0位置默认可以被认为是有序的)
- 从1位置开始取出元素, 并且判断该元素的大小和0位置进行比较, 如果1位置元素小于0位置元素, 那么交换, 否则不交换.
- 上面步骤执行完成后, 0 - 1位置已经排序好.
- 取出2位置的元素, 和1位置进行比较:
- 如果2位置元素大于1位置元素, 说明2位置不需要任何动作. 0 - 1 - 2已经排序好.
- 如果2位置元素小于1位置元素, 那么将1移动到2的位置, 并且2继续和0进行比较.
- 如果2位置元素大于0位置的元素, 那么将2位置放置在1的位置, 排序完成. 0 - 1 - 2搞定.
- 如果2位置元素小于1位置的元素, 那么将0位置的元素移动到1位置, 并且将2位置的元素放在0位置, 0 - 1 - 2搞定.
- 按照上面的步骤, 依次找到最后一个元素, 整个数组排序完成.
list = [3, 7, 2, 6, 9, 1, 4]
for i in range(1,len(list)):
temp = list[i]
while i > 0 and list[i-1] >temp:
list[i] = list[i-1]
i -=1
list[i] = temp
print(list)
归并排序
基本思想与过程:先递归的分解数列,再合并数列(分治思想的典型应用)
(1)将一个数组拆成A、B两个小组,两个小组继续拆,直到每个小组只有一个元素为止。
(2)按照拆分过程逐步合并小组,由于各小组初始只有一个元素,可以看做小组内部是有序的,合并小组可以被看做是合并两个有序数组的过程。
(3)对左右两个小数列重复第二步,直至各区间只有1个数。
下面对数组【42,20,17,13,28,14,23,15】进行归并排序,模拟排序过程如下:
第一步:拆分数组,一共需要拆分三次;
第一次拆成【42,20,17,13】,【28,14,23,15】,
第二次拆成【42,20】,【17,13】,【28,14】,【23,15】,、
第三次拆成【42】,【20】,【17】,【13】,【28】,【14】,【23】,【15】;
第二步:逐步归并数组,采用合并两个有序数组的方法
第一次归并为【20,42】,【13,17】,【14,28】,【15,23】
第二次归并为【13,17,20,42】,【14,15,23,28】,
第三次归并为【13, 14, 15, 17, 20, 23, 28, 42】
def merge(left,right):
result = []
i = j =0
while i <len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result += left[i:]
result += right[j:]
return result
#归并排序
def merge_sort(arr):
length = len(arr)
if length > 1:
index = length // 2
left = arr[:index]
right = arr[index:]
return merge(merge_sort(left), merge_sort(right))
else:
return arr
快速排序
快速排序的思想:
- 快速排序最重要的思想是分而治之.
- 比如我们有这样一堆数字需要排序: [13,81,92,43,65,31,57,26,75,0]
- 第一步: 从其中选出了65. (其实可以是选出任意的数字, 我们以65举个栗子)
- 第二步: 我们通过算法: 将所有小于65的数字放在65的左边, 将所有大于65的数字放在65的右边.
- 第三步: 递归的处理左边的数据.(比如你选择31来处理左侧), 递归的处理右边的数据.(比如选择75来处理右侧, 当然选择81可能更合适)
- 最终: 排序完成
def quick_sort(list):
if len(list) <= 1:
return list
mid = list[len(list) // 2]
left = [x for x in list if x < mid]
middle = [x for x in list if x == mid]
right = [x for x in list if x > mid]
return quick_sort(left)+middle+quick_sort(right)
list=[13,81,92,43,65,31,57,26,75,0]
newlist=quick_sort(list)
print(newlist)
顺序搜索
从数据结构的第一个元素开始逐个检查,直到找到目标值或遍历完整个数据集。适用于数组和链表等基本数据结构。
二分搜索
针对有序数组进行搜索,通过不断将搜索范围减半来提高查找效率。每次都将搜索区间划分为两部分,并基于中间元素与目标值的比较决定是在左半边还是右半边继续搜索。
思路:
初始化:给定一个已排序的列表或数组 arr 和一个要查找的目标值 target。
定义查找区间:设置两个指针,分别代表待查找区间的起始(left)和结束(right)位置。
循环条件:当左边界不大于右边界时,继续循环。
计算中间位置:取区间的中间位置 mid,一般计算方式为 (left + right) // 2。
比较中间元素:检查 arr[mid] 是否等于目标值 target。
若相等,则找到了目标值,返回 mid 作为目标值的索引。
若 arr[mid] 小于 target,说明目标值可能在 mid 右侧,因此更新左边界为 mid + 1。
若 arr[mid] 大于 target,说明目标值可能在 mid 左侧,因此更新右边界为 mid - 1。
循环结束:如果跳出循环,意味着没有找到目标值,返回 -1 或者一个特殊值表示未找到。
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (right + left) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1 # 没有找到目标值时返回 -1