我简单说一下下面代码中对于下标的使用,这样好理解
前提:
- 小括号
()
,不包含边界值- 中括号
[]
,包含边界值
举个例子:(2,6]
表示的范围为下标2(不包含)到下标6(包含)
先看一下代码(列表长度为偶数,假定为10,待查找列表增序,返回待查找值的下标)
def _binarySearch(key, a, lo, hi):
if lo >= hi:
return -1
mid = (lo + hi) // 2
if(key < a[mid]):
return _binarySearch(key, a, lo, mid)
elif(key > a[mid]):
return _binarySearch(key, a, mid+1, hi)
else:
return mid
def binarySearch(key, a):
return _binarySearch(key, a, 0, len(a))
if __name__ == '__main__':
a = [1, 99, 101, 102, 203, 305, 415, 523, 666, 1000]
key = int(input('请输入待查找关键字:'))
print("关键字{0}位于列表索引(-1表示不存在):{1}".format(key, binarySearch(key, a)))
再看上面代码中对于下标的玩法:
binarySearch()
函数中对递归函数_binarySearch()
的调用是这样的return _binarySearch(key, a, 0, len(a))
_binarySearch(key, a, lo, hi)
中lo
代表左边界,包含hi
代表右边界,不包含[lo, hi)
_binarySearch()
递归函数中关于中间下标mid = (lo + hi) // 2
的取值是有含义的mid = (0+10)//2 = 5
待查找值 < a[5]
,下标范围缩小为[0,5)
待查找值 > a[5]
,下标范围缩小为[6,10)
待查找值 = a[5]
,找到了呀,递归结束,返回待查找值的下标
mid = (0+5)//2 = 2
,(key = 102) > (a[2] = 101)
[3,5)
mid = (3+5)//2 = 4
,(key = 102) < (a[4] = 203)
[3,4)
mid = (3+4)//2 = 3
,key = 102
,找到了呀,递归结束,返回待查找值的下标_binarySearch()
递归函数的中止条件是左边界不小于右边界[4,4)
lo >= hi
return -1
意为没找到值下面代码对于下标的玩法和上面的有一些不一样:
low
,右边界high
都是包含值的mid = (low + high)//2
,mid取值偏左mid = (low + high)//2 + 1
,不会影响代码的逻辑列表长度为偶数,假定为10,待查找列表增序,返回待查找值的下标
def binarySearch(key, a):
low = 0
high = len(a) -1
while low <= high:
mid = (low + high)//2
if(key > a[mid]):
low = mid +1
elif(key < a[mid]):
high = mid -1
else:
return mid
return -1
if __name__ == '__main__':
a = [1, 99, 101, 102, 203, 305, 415, 523, 666, 1000]
key = int(input('请输入待查找关键字:'))
print("关键字{0}位于列表索引(-1表示不存在):{1}".format(key, binarySearch(key, a)))
如若待查找列表是降序,也并不难:
def binarySearch(key, a):
low = 0
high = len(a) -1
while low <= high:
mid = (low + high)//2
if(key < a[mid]):
low = mid +1
elif(key > a[mid]):
high = mid -1
else:
return mid
return -1
if __name__ == '__main__':
a = [1000, 900, 800, 700, 600, 500, 400, 300, 200, 100]
while 1:
key = int(input('请输入待查找关键字:'))
print("关键字{0}位于列表索引(-1表示不存在):{1}".format(key, binarySearch(key, a)))
def bubbleSort(a):
for i in range(len(a)-1, -1, -1):
for j in range(i):
if a[j] > a[j+1]:
a[j],a[j+1] = a[j+1],a[j]
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
bubbleSort(a)
print(a)
if __name__ == '__main__':main()
分析:
列表长度:10
外层循环i : [9, 0]
,实际上i的范围可以缩小为[9, 1]
,减
内存循环j : [0, i)
,增
当 i 为 9 时,内层循环中的条件交换,将最大值像冒泡泡一样(j:从下标小处向下标大处),冒到下标最大的位置
当 i 为 8 时,将第二大值像冒泡泡一样,冒到下标第二大的位置
依此类推
a[j] < a[j+1]
)def bubbleSort(a):
for i in range(len(a)-1, 0, -1):
for j in range(i):
if a[j] < a[j+1]:
a[j],a[j+1] = a[j+1],a[j]
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
bubbleSort(a)
print(a)
if __name__ == '__main__':main()
def bubbleSort(a):
for i in range(0, len(a)):
for j in range(len(a)-1, i, -1):
if a[j-1] > a[j]:
a[j],a[j-1] = a[j-1],a[j]
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
bubbleSort(a)
print(a)
if __name__ == '__main__':main()
分析:
列表长度:10
外层循环i : [0, 9]
,增
内存循环j : [9, i)
,减
当 i 为 0 时,内层循环中的条件交换,将最小值像冒泡泡一样(j:从下标大处向下标小处),冒到下标最小的位置
当 i 为 1 时,将第二小值像冒泡泡一样,冒到下标第二小的位置
依此类推
a[j] < a[j+1]
)def bubbleSort(a):
for i in range(0, len(a)):
for j in range(len(a)-1, i, -1):
if a[j-1] < a[j]:
a[j],a[j-1] = a[j-1],a[j]
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
bubbleSort(a)
print(a)
if __name__ == '__main__':main()
多说一嘴,与本文主题无关,不过,可以多了解一下嘛
- 冒泡和插入排序的效率都和逆序对的个数有关,每次相邻元素交换,刚好消去一对逆序对
- 如果序列基本有序,则插入排序简单高效
- 定理:任何仅以交换相邻两元素来排序的算法,其平均时间复杂度都是n的平方
- 所以要提高算法效率,我们可以:每次消去不止一对逆序对——>每次交换相隔较远的两个元素
- 于是:希尔排序(有插入排序的优点,又解决了插入排序每次交换只能消去一个逆序对的缺点,适用于上万数据的排序,但希尔排序还存在一个问题:增量元素不互质,则小增量可能根本不起作用,所以诞生了 Hibbard增量序列和 Sedgewick增量序列。本文不涉及这个算法…)
def selectionSort(a):
for i in range(0, len(a)-1):
for j in range(i+1, len(a)):
if(a[j] < a[i]):
a[i],a[j] = a[j],a[i]
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
selectionSort(a)
print(a)
if __name__ == '__main__':main()
分析:
列表长度:10
外层循环i : [0, 9)
,增
内存循环j : [i+1, 9]
,增
当 i 为 0 时,内存循环就是将[1, 9]范围内的数值全部与a[0]比较,小则交换(即:选择出最小值,给到下标最小处:这就是选择)
当 i 为 1 时,内存循环就是将[2, 9]范围内的数值全部与a[0]比较…
def selectionSort(a):
for i in range(0, len(a)-1):
for j in range(i+1, len(a)):
if(a[j] > a[i]):
a[i],a[j] = a[j],a[i]
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
selectionSort(a)
print(a)
if __name__ == '__main__':main()
def selectionSort(a):
for i in range(len(a)-1, 0, -1):
for j in range(0, i):
if(a[j] > a[i]):
a[i],a[j] = a[j],a[i]
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
selectionSort(a)
print(a)
if __name__ == '__main__':main()
分析:
列表长度:10
外层循环i : [9, 1]
,减
内存循环j : [0, i-1]
,增
当 i 为 9 时,内存循环就是将[0, 8]范围内的数值全部与a[9]比较,大则交换(即:选择出最大值,给到下标最大处)
当 i 为 8 时,内存循环就是将[0, 7]范围内的数值全部与a[8]比较…
def selectionSort(a):
for i in range(len(a)-1, 0, -1):
for j in range(0, i):
if(a[j] < a[i]):
a[i],a[j] = a[j],a[i]
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
selectionSort(a)
print(a)
if __name__ == '__main__':main()
def insertSort(a):
for i in range(1, len(a)):
for j in range(i, 0, -1):
if(a[j-1] > a[j]):
a[j-1],a[j] = a[j],a[j-1]
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
insertSort(a)
print(a)
if __name__ == '__main__':main()
分析:
上面的代码不是插入排序哦!!!,用来理解过渡的
列表长度:10
外层循环i : [1, 9]
,增
内存循环j : [i, 1]
,减
def insertSort(a):
for i in range(1, len(a)):
j = i
while (j > 0) and (a[j-1] > a[j]):
a[j-1],a[j] = a[j],a[j-1]
j -= 1
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
insertSort(a)
print(a)
if __name__ == '__main__':main()
分析:
列表长度:10
外层循环i : [1, 9]
,增
内层循环,有两个判断条件,减(减多少次,不定次,这就是比上面代码的更优化处)
就像玩扑克牌时,我们手动将扑克牌增序排序:从左到右,看到小的就插在前面的合适位置
def insertSort(a):
for i in range(1, len(a)):
j = i
while (j > 0) and (a[j-1] < a[j]):
a[j-1],a[j] = a[j],a[j-1]
j -= 1
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
insertSort(a)
print(a)
if __name__ == '__main__':main()
def insertSort(a):
for i in range(len(a)-2, -1, -1):
j = i
while (j < len(a)-1) and (a[j+1] < a[j]):
a[j+1],a[j] = a[j],a[j+1]
j += 1
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
insertSort(a)
print(a)
if __name__ == '__main__':main()
分析:
列表长度:10
外层循环i : [8, 0]
,减
内层循环,增
从右向左手动将扑克牌增序排序,看到大的就插在后面的合适位置
def insertSort(a):
for i in range(len(a)-2, -1, -1):
j = i
while (j < len(a)-1) and (a[j+1] > a[j]):
a[j+1],a[j] = a[j],a[j+1]
j += 1
def main():
a = [2, 97, 86, 64, 50, 80, 3, 71, 8, 76]
insertSort(a)
print(a)
if __name__ == '__main__':main()
def merge(left, right):
merged = []
i, j = 0, 0
left_len, right_len = len(left), len(right)
while i < left_len and j < right_len:
if left[i] <= right[j]:
merged.append(left[i])
i += 1
else:
merged.append(right[j])
j += 1
merged.extend(left[i:])
merged.extend(right[j:])
return merged
def mergeSort(a):
if len(a) <= 1:
return a
mid = len(a) // 2
left = mergeSort(a[:mid])
right = mergeSort(a[mid:])
return merge(left, right)
def main():
a = [59, 12, 77, 64, 72, 69, 46, 89, 31, 9]
print(mergeSort(a))
if __name__ == '__main__':main()
思路很存粹,
运用递归将待排序列表进行二分,分到只剩一个数据(即:mergeSort()
)
然后,到了一个临界点,
mergeSort()
递归开始回溯的时候,执行merge()
(作用:将两个相同排序的序列,进行排序)
补充:这三句这么理解
mid = len(a) // 2
left = mergeSort(a[:mid])
right = mergeSort(a[mid:])
(例子)倘若序列长度为偶数:
>>> a = [0,1,2,3,4,5,6,7,8,9]
>>> a[:5]
[0, 1, 2, 3, 4]
>>> a[5:]
[5, 6, 7, 8, 9]
(例子)倘若序列长度为奇数:
>>> a = [0,1,2,3,4]
>>> a[:2]
[0, 1]
>>> a[2:]
[2, 3, 4]
def quickSort(a, low , high):
i = low
j = high
if i >= j:
return a
key = a[i]
while i < j:
while i < j and a[j] >= key:
j -= 1
a[i] = a[j]
while i < j and a[i] <= key:
i += 1
a[j] = a[i]
a[i] = key
quickSort(a, low, i-1)
quickSort(a, j+1, high)
def main():
a = [59, 12, 77, 64, 72, 69, 46, 89, 31, 9]
quickSort(a, 0, len(a)-1)
print(a)
if __name__ == '__main__':main()