任务3:5~6天
python 十大经典排序算法
1.学习目标
1.1 排序
实现归并排序、快速排序、插入排序、冒泡排序、选择排序、堆排序(选做)(完成leetcode上的返回滑动窗口中的最大值(239),这是上一期第三天的任务进行保留(涉及队列可以对第二天进行整理复习))
编程实现 O(n) 时间复杂度内找到一组数据的第 K 大元素
1.2 二分查找
1.2.1 实现一个有序数组的二分查找算法
1.2.2实现模糊二分查找算法(比如大于等于给定值的第一个元素)
1.3 对应的 LeetCode 练习题
Sqrt(x) (x 的平方根)
英文版:Loading…
中文版:Loading…
2.学习内容
2.1 排序
所谓排序就是将一组无序的记录序列调整为有序的记录序列。
- 选择排序:主要包括简单选择排序和堆排序
- 插入排序:简单插入排序、希尔排序
- 交换排序:冒泡排序、快速排序
- 归并排序
- 非比较排序:计数排序、桶排序、基数排序属于非比较排序,算法时间复杂度O(n), 属于空间换时间。
2.1.1 归并排序
基本思想:
将数组A[0…n-1]中的元素分成两个子数组,A1[0…n/2] 和A2[n/2+1…n-1]。分别对这两个子数组单独排序(递归),然后将已排序的两个子数组归并成一个含有n个元素的有序数组。
合并的过程:
比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。
代码实现:
def merge_sort(lst):
if len(lst) <= 1:
return lst
mid = len(lst) //2
left = merge_sort(lst[:mid])
right = merge_sort(lst[mid:])
return merge(left, right)
def merge(left, right):
res = []
i = j = 0
while i <len(left) and j <len(right):
if left[i] <= right[j]:
res.append(left[i])
i += 1
else:
res.append(right[j])
j += 1
res += left[i:]
res += right[j:]
return res
2.1.2 快速排序
快速排序是一种基于划分的排序方法
划分Partition思想:
-
选取待排序集合A中的某个元素t,按照与t的大小关系重新整理A中元素,使得整理后的序列中所有在t以前出现的元素均小于t,而所有出现在t以后的元素均大于等于t;元素t称为划分元素。
-
反复地对A进行划分达到排序的目的。
划分算法:
对于数组A[0…n-1]:
设置两个变量i, j:i=0, j=n-1
以A[0]为关键数据,即key=A[0]
从j开始向前搜索,直到找到第一个小于key的值a[j],将a[i] = a[j];
从i开始向后搜索,直到找到第一个大于等于key的值a[i],a[j] = a[i];
重复第3、4步,直到i≥j.
def quick_sort(lists):
less = []
pivotList = []
more = []
if len(lists) <= 1:
return lists
else:
pivot = lists[0] # 将第一个值作为基准值
for i in lists:
if i < pivot:
less.append(i)
elif i > pivot:
more.append(i)
else:
pivotList.append(i)
less = quick_sort(less)
more = quick_sort(more)
return less + pivotList + more
def QuickSort(myList,start,end):
#判断low是否小于high,如果为false,直接返回
if start < end:
i,j = start,end
#设置基准数
base = myList[i]
while i < j:
#如果列表后边的数,比基准数大或相等,则前移一位直到有比基准数小的数出现
while (i < j) and (myList[j] >= base):
j = j - 1
#如找到,则把第j个元素赋值给第个元素i,此时表中i,j个元素相等
myList[i] = myList[j]
#同样的方式比较前半区
while (i < j) and (myList[i] <= base):
i = i + 1
myList[j] = myList[i]
#做完第一轮比较之后,列表被分成了两个半区,并且i=j,需要将这个数设置回base
myList[i] = base
#递归前后半区
QuickSort(myList, start, i - 1)
QuickSort(myList, j + 1, end)
return myList
2.1.3 插入排序
插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据。 算法适用于少量数据的排序,时间复杂度为O(n^2)。
插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。 步骤如下:
-
假设序列的第一个数是排序好的,(如果序列长度为1,那就更好了,不用排序了)。
-
取出已排序的数的下一个数,当前这个数是需要排序的(未排序)。用当前这个数与之前排序好的数进行比较,比较的顺序是从后往前。
-
如果当前已经排序的数比未排序的数大,则已经排序的数往后挪一个位置,空出当前已经排序位置,下次比较的已经排序好的数是当前已经排序好的数的前一个数。
-
重复步骤3,直到未排序的数小于已排序的数,将未排序的数插入到空出的位置。
-
重复2-5 ,直到所有数都排序好
def insert_sort(lists):
size = len(lists)
for i in range(1, size):
key = lists[i]
j = i-1
while j >= 0:
if lists[j] > key:
lists[j+1] = lists[j]
lists[j] = key
j -= 1
return lists
2.1.4 冒泡排序
重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。
走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
时间复杂度为O(n^2)
def bubble_sort(lists):
n = len(lists)
for i in range(n):
for j in range(1, n-i): # 每一次冒泡将最大的数交换到数列的最后一位
if lists[j-1] > lists[j]:
lists[j - 1],lists[j] = lists[j], lists[j-1]
return lists
2.1.5 选择排序
从待排序的数据元素中选出最小的一个元素,存放在序列的起始位置,直到全部带排序的数据元素排完。
def select_sort(lists):
n = len(list)
for i in range(n):
for j in range(i,n):
if list[i]>list[j]:
list[i],list[j]=list[j],list[i]
return lists
2.1.6 堆排序
利用数组的特点快速定位指定索引的元素
堆分为大根堆和小根堆,是完全二叉树
基本思想:
-
最大堆调整(adjust_heap):将堆的末端子节点作调整,使得子节点永远小于父节点。这是核心步骤,在建堆和堆排序都会用到。比较i的根节点和与其所对应i的孩子节点的值。当i根节点的值比左孩子节点的值要小的时候,就把i根节点和左孩子节点所对应的值交换,当i根节点的值比右孩子的节点所对应的值要小的时候,就把i根节点和右孩子节点所对应的值交换。然后再调用堆调整这个过程,可见这是一个递归的过程。
-
建立最大堆(Build_Heap):将堆所有数据重新排序。建堆的过程其实就是不断做最大堆调整的过程,从len/2出开始调整,一直比到第一个节点。
-
堆排序(Heap_Sort):移除位在第一个数据的根节点,并做最大堆调整的递归运算。堆排序是利用建堆和堆调整来进行的。首先先建堆,然后将堆的根节点选出与最后一个节点进行交换,然后将前面len-1个节点继续做堆调整的过程。直到将所有的节点取出,对于n个数我们只需要做n-1次操作。
def heap_sort(lists):
# 堆排序
size = len(lists)
build_heap(lists, size)
for i in range(0, size)[::-1]:
lists[0], lists[i] = lists[i], lists[0]
adjust_heap(lists, 0, i)
return lists
def adjust_heap(lists, i, size):
# 调整堆
lchild = 2 * i + 1
rchild = 2 * i + 2
maxi = i
if lchild < size and lists[maxi] < lists[lchild]:
maxi = lchild
if rchild < size and lists[maxi] < lists[rchild]:
maxi = rchild
if maxi != i:
# 如果做了堆调整则maxi的值等于左节点或者右节点的,这个时候做对调值操作
lists[maxi], lists[i] = lists[i], lists[maxi]
adjust_heap(lists, maxi, size)
def build_heap(lists, size):
# 堆的构建
for i in range(0, int(size/2))[::-1]:
adjust_heap(lists, i, size)
排序算法的比较
2.1.7 编程实现 O(n) 时间复杂度内找到一组数据的第 K 大元素
二分+quick sort的思想,随机取一个pivot,大于小于等于它的分别放起来,然后看看大于它的有没有k个,没有就可能在等于或者小于里面
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
pivot = nums[0]
smaller = [num for num in nums if num < pivot]
greater = [num for num in nums if num > pivot]
equal = [num for num in nums if num == pivot]
if len(greater) >= k:
return self.findKthLargest(greater, k)
if len(equal) >= k-len(greater):
return equal[0]
else:
return self.findKthLargest(smaller, k-len(equal)-len(greater))
2.2 二分查找
2.1 实现一个有序数组的二分查找算法
def binary_search(nums, x):
if not nums:
return
l, r = 0, len(nums)-1
while l <= r:
mid = (l+r) // 2
if nums[mid] > x:
r = mid -1
elif nums[mid] < x:
l = mid + 1
else:
return mid
return -1
2.2 实现模糊二分查找算法(比如大于等于给定值的第一个元素)
2.3 对应的 LeetCode 练习题
class Solution:
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
r = x
while r*r >x:
r =int((r+x/r)/2)
return r