目录
第一章 复杂度与简单排序算法
三种简单排序
1. 冒泡排序
从左到右两两比较并交换,把最大的交换到最后一个位置
从最后一个位置开始填坑
def bubbleSort(arr,n):
if len(arr) <2:
return arr
for j in range(n-1, 0, -1):
for i in range(j):
if arr[i] > arr[i+1]:
arr[i], arr[i+1] = arr[i+1],arr[i]
return arr
n = int(input())
nums = list(map(int, input().split()))
res = bubbleSort(nums, n)
res = [str(num) for num in res]
print(' '.join(res))
2. 选择排序
从第一个位置开始填坑
每次选择最小的放到第i个位置
def select(n, nums):
if n < 2:
return nums
for i in range(0, n-1):
minIndex = i
for j in range(i+1, n):
if nums[j] < nums[minIndex]:
minIndex = j
nums[i], nums[minIndex] = nums[minIndex], nums[i]
return nums
n = int(input())
nums = list(map(int, input().split()))
res = select(n, nums)
#res = [str(num) for num in res]
#print(' '.join(res))
print(*res)
3. 插入排序
假设前面 i - 1 个数已经有序,将第 i 个数通过交换的方式插入到前面的正确位置,使得前 i 个数都有序的。
def insertSort(n, arr):
if n < 2:
return arr
for i in range(1,n):
for j in range(i, 0, -1):
if arr[j-1] > arr[j]: #凡是大的都换到后面;两两交换
arr[j-1], arr[j] = arr[j], arr[j-1]
#else:
#break 遇到第一个<=当前的就可提前结束循环,因为前面有序,都比他小
return arr
n = int(input())
nums = list(map(int, input().split()))
res = insertSort(n, nums)
res = [str(num) for num in nums]
print(' '.join(res))
二分查找
4. 二分查找某数
在有序数组中找到目标值 k 的索引,若不存在则返回 -1,存在多个则返回最小的索引
相当于找到第一个比k小的数, 然后加一
def search(n, k, nums):
i, j = 0, n - 1
while i <= j:
m = i + ((j - i) >> 1) #注意这里不能少了括号,+比>>的优先级高
if nums[m] >= k: j = m - 1 #只要比目标大就要继续向左缩小范围
else: i = m + 1
index = j + 1
return index if nums[index] == k else -1
n, k = list(map(int, input().split())) # n, k= [int(i) for i in input().split]
nums = list(map(int, input().split()))
print(search(n, k, nums))
5. 查找某个位置
你需要输入一个n,一个数k,然后输入一个长度为n个大小的数组arr,然后你需要在arr上找满足>=K的最左位置,并且输出这个位置,如果不存在这个位置就输出-1。
示例1
输入
5 1
0 0 2 4 6
输出
2
思路:同样是找到第一个比k小的数, 然后加一,要比目标大就要继续向左缩小范围
def search(n, k, nums):
if nums[n-1] < k: return -1
i, j = 0, n - 1
while i <= j:
m = i + ((j - i) >> 1)
if nums[m] >= k: j = m - 1
else: i = m + 1
index = j + 1
return index
n, k = list(map(int, input().split())) # n, k= [int(i) for i in input().split]
nums = list(map(int, input().split()))
print(search(n, k, nums))
6. 局部最小值问题
定义局部最小的概念。arr长度为1时,arr[0]是局部最小。arr的长度为N(N>1)时,如果arr[0] < arr[1],那么arr[0]是局部最小;
如果arr[N-1]<arr[N-2],那么arr[N-1]是局部最小;如果0<i<N-1,既有arr[i] < arr[i-1],又有arr[i] < arr[i + 1],那么arr[i]
是局部最小。给定无序数组arr,已知arr中任意两个相邻的数都不相等,只需要返回arr中任意一个局部最小出现的位置即可,如果不存在这个位置就输出-1。
方法一:二分查找
def binarySearch(n, nums):
if n < 2: return 0
i, j = 0, n-1
if nums[i] < nums[i+1]: return 0
if nums[j] < nums[j-1]: return j
while i <= j:
m = i + ((j - i) >> 1)
if nums[m] < nums[m-1] and nums[m] < nums[m+1]: return m
elif nums[m] < nums[m+1]: j = m - 1
else: i = m + 1 # nums[m] < nums[m-1]
return -1
n = int(input())
nums = list(map(int, input().split()))
res = binarySearch(n, nums)
print(res)
一道复杂度很低用异或运算解决的题
7. 一个数出现奇数次
一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这一个数?
方法一:利用异或
n = int(input())
nums = list(map(int, input().split()))
eor = 0
for num in nums:
eor ^= num
print(eor)
第二章 O(N logN)的排序
归并排序
1. 归并排序
- 数组归并排序
def process(arr, L, R):
if L == R:
return
mid = L + ((R - L) >> 1)
process(arr, L, mid)
process(arr, mid + 1, R)
merge(arr, L, mid, R)
def merge(arr, L, mid, R):
tmp = []
p1, p2 = L, mid + 1
while p1 <= mid and p2 <= R:
if arr[p1] <= arr[p2]:
tmp.append(arr[p1])
p1 += 1
else:
tmp.append(arr[p2])
p2 += 1
while p1 <= mid:
tmp.append(arr[p1])
p1 += 1
while p2 <= R:
tmp.append(arr[p2])
p2 += 1
arr[L:R+1] = tmp #注意这里直接return tmp是不对的,因为只用改变L到R+1,不能把arr都改了
n = int(input())
arr = list(map(int, input().split()))
process(arr, 0, n - 1)
print(*arr)
- 单链表的归并排序
class Solution:
def sortInList(self , head: ListNode) -> ListNode:
if not head or not head.next: return head #空节点或者节点为一,什么都不用做,返回
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
mid = slow.next
slow.next = None
left = self.sortInList(head)
right = self.sortInList(mid)
dummy = cur = ListNode(0)
while left and right:
if left.val <= right.val:
cur.next = left
left = left.next
else:
cur.next = right
right = right.next
cur = cur.next
cur.next = left if left else right
return dummy.next
堆排序
2. 堆排序
1)建堆;2)交换数据;3)向下调整。
#迭代写法二:左神
def heapify(arr, n, i): #heapify就是不断地在两个孩子中找出最大的那个,然后与父节点交换
left = i * 2 + 1
while left < n:
largest = left + 1 if left + 1 < n and arr[left+1] > arr[left] else left
largest = i if arr[i] > arr[largest] else largest
if largest == i:
break
arr[i], arr[largest] = arr[largest], arr[i]
i = largest
left = 2 * i + 1
def heapsort(arr):
n = len(arr)
#先建立大根堆
for i in range(n-1, -1, -1):
heapify(arr, n, i) #自底向下遍历,看所有子树的根节点能否下沉
for i in range(n-1, 0, -1):
arr[0], arr[i] = arr[i], arr[0] #arr[0] 是最大的,不断地不最大的换到末尾,然后长度减一
heapify(arr, i, 0) #将换上来的数做heapify,往下沉,以变回最大堆
n = int(input())
nums = list(map(int, input().split()))
heapsort(nums)
print(*nums)
补充1:
heapify写法
#迭代写法:mycode
def heapify(arr, n, i):
left = i * 2 + 1
right = i * 2 + 2
while left < n:
large = right if right < n and arr[left] < arr[right] else left
if arr[i] >= arr[large]:
break
else:
arr[i], arr[large] = arr[large], arr[i]
i = large
left = i * 2 + 1
right = i * 2 + 2
#递归写法:
def heapify(arr, n, i):
left = 2 * i + 1
right = 2 * i + 2
largest = i
if left < n and arr[left] > arr[i]:
largest = left
if right < n and arr[right] > arr[largest]:
largest = right
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
补充2:
用heapInsert的方法建堆
def heapsort(arr):
n = len(arr)
arr_heap = []
for num in arr:
heapInsert(arr_heap, num)
for i in range(n-1, 0, -1):
arr_heap[0], arr_heap[i] = arr_heap[i], arr_heap[0]
heapify(arr_heap, i, 0) #将换上来的数做heapify,往下沉,以变回最大堆
return arr_heap
def heapInsert(arr, num):
i = len(arr)
arr.append(num)
parent = (i - 1) >> 1 #不断地与父节点比较,大于父节点则交换
while parent >= 0:
if arr[parent] < arr[i]:
arr[parent], arr[i] = arr[i], arr[parent]
i = parent
parent = (i - 1) >> 1
else: break
n = int(input())
nums = list(map(int, input().split()))
nums = heapsort(nums)
print(*nums)
3. 荷兰国旗问题
牛牛今天带来了一排气球,气球有n个,然后每一个气球里面都包含一个数字,牛牛是一个善于思考的人,于是他就想到了一个问题,
牛牛随便给你一个值K,这个值在这些气球中不一定存在,聪明的你需要把气球中包含的数字是小于K的放到这排气球的左边,大于K的放到气球的右边,
等于K的放到这排气球的中间,最终返回一个整数数组,其中只有两个值,分别是气球中包含的数字等于K的部分的左右两个下标值,如果气球中没有K这个数字就输出-1,-1。
def helper(arr, target):
less = -1
more = len(arr)
index = 0
while index < more:
if arr[index] < target:
less += 1
arr[index], arr[less] = arr[less], arr[index]
index += 1
elif arr[index] > target:
more -= 1
arr[index], arr[more] = arr[more], arr[index]
else:
index += 1
return [-1, -1] if less + 1 > more - 1 else [less + 1, more - 1]
n, k = [int(i) for i in input().split()]
nums = [int(i) for i in input().split()]
res = helper(nums, k)
print(res[0],res[1]) #print(*res)
快速排序
1. 快速排序
def quickSort(arr, L, R):
if L >= R: return
i, j = L, R
while i < j:
while i < j and arr[j] >= arr[L]: j -= 1
while i < j and arr[i] <= arr[L]: i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[L], arr[i] = arr[i], arr[L]
quickSort(arr, L, i-1)
quickSort(arr, i+1, R)
n = int(input())
nums = list(map(int, input().split()))
quickSort(nums, 0, n-1)
print(*nums)
第三章 桶排序及排序内容总结
1. 计数排序
def countSort(nums, ):
bucket = [0] * (max(nums)+1)
for i in nums:
bucket[i] += 1
index = 0
for i, count in enumerate(bucket):
for _ in range(count):
nums[index] = i
index += 1
n = int(input())
nums = list(map(int, input().split()))
countSort(nums)
print(*nums)
2, 基数排序
def getDigit(n, d): #取出整数n第d位数
return int((n//10**(d-1)) % 10) #将第d位放到个位,在取mod10得到个位数
def radixSort(arr, n, digit): #digit:最大的数有多少位
radix = 10
i, j = 0, 0
bucket = [0] * n
for d in range(1, digit + 1): #有多少位就进出多少次
count = [0] * radix
for num in arr:
j = getDigit(num, d)
count[j] += 1
for i in range(1, radix):
count[i] = count[i] + count[i - 1]
for i in range(n-1, -1, -1): # 遍历arr的数
j = getDigit(arr[i], d) #看当前数的第d位是什么
bucket[count[j]-1] = arr[i]
count[j] -= 1
arr[:] = bucket[:]
n = int(input())
nums =list(map(int, input().split()))
digit = len(str(max(nums)))
radixSort(nums, n, digit)
print(*nums)
第四章 链表
1. 判断一个链表是否为回文结构
方法一:使用栈
class Solution:
def isPail(self , head: ListNode) -> bool:
# write code here
cur = head
stack = []
while cur:
stack.append(cur.val)
cur = cur.next
while head:
if head.val != stack.pop():
return False
head = head.next
return True
#或者
class Solution:
def isPail(self , head: ListNode) -> bool:
# write code here
cur = head
stack = []
while cur:
stack.append(cur.val)
cur = cur.next
return stack == stack[::-1]
方法二:快慢指针,找到中点
空间复杂度降为O(1)
class Solution:
def isPail(self , head: ListNode) -> bool:
if not head or not head.next: return True
fast = slow = head
while fast.next and fast.next.next: #如果nexi是空的,那么next.next肯定也是空(实际上是没有)
fast = fast.next.next
slow = slow.next
pre = None
cur = slow.next
slow.next = None
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
L, R = head, pre
while L and R:
Lpre = L
if L.val != R.val:
return False
R = R.next
L = L.next
return True
2. 链表划分
给出一个长度为 n 的单链表和一个值 x ,单链表的每一个值为 listi ,请返回一个链表的头结点,要求新链表中小于 x 的节点全部在大于等于 x 的节点左侧,并且两个部分之内的节点之间与原来的链表要保持相对顺序不变。
方法一:遍历到list中做partition(荷兰国旗问题)再链接,但相对顺序会乱掉
class Solution:
def partition(self , head: ListNode, x: int) -> ListNode:
if not head or not head.next: return head
nums = []
while head:
nums.append(head.val)
head = head.next
L, M = -1, len(nums)
i = 0 #当前要处理得数
while i < M:
if nums[i] < x:
L += 1
nums[i], nums[L] = nums[L], nums[i]
i += 1
elif nums[i] == x:
i += 1
else:
M -= 1
nums[i], nums[M] = nums[M], nums[i]
res = cur= ListNode(nums[0])
for num in nums[1:]:
cur.next = ListNode(num)
return res
方法二:用六个头节点将其划分保存
额外空间仍然是O(1)的
#课程的代码,分三段,但题目要求是分两段
class Solution:
def partition(self , head: ListNode, x: int) -> ListNode:
sH = None
sT = None
eH = None
eT = None
bH = None
bT = None
while head:
tmp = head.next
head.next = None
if head.val < x:
if sH == None:
sH = sT = head
else:
sT.next = head
sT = sT.next
elif head.val == x:
if eH == None:
eH = eT = head
else:
eT.next = head
eT = eT.next
else:
if bH == None:
bH = bT = head
else:
bT.next = head
bT = bT.next
head = tmp
if eH:
eT.next = bH if bH else None
if sH:
sT.next = eH if eH else (bH if bH else None)
return sH if sH else (eH if eH else bH)
正解:
class Solution:
def partition(self , head: ListNode, x: int) -> ListNode:
sH = None
sT = None
eH = None
eT = None
while head:
tmp = head.next
head.next = None
if head.val < x:
if sH == None:
sH = sT = head
else:
sT.next = head
sT = sT.next
else:
if eH == None:
eH = eT = head
else:
eT.next = head
eT = eT.next
head = tmp
if sH:
sT.next = eH if eH else None
return sH if sH else (eH if eH else None)
3. 复制含有随机指针节点的链表
方法一:哈希表
方便找到旧节点对应的新节点
# -*- coding:utf-8 -*-
# class RandomListNode:
# def __init__(self, x):
# self.label = x
# self.next = None
# self.random = None
class Solution:
# 返回 RandomListNode
def Clone(self, pHead):
if not pHead: return
cur = pHead
dic ={}
while cur:
dic[cur] = RandomListNode(cur.label)
cur = cur.next
cur = pHead
while cur:
dic.get(cur).next = dic.get(cur.next)
dic.get(cur).random = dic.get(cur.random)
cur = cur.next
return dic[pHead]
方法二:将新节点接在旧节点的下一个也可以方便找到
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
if not head: return
cur = head
while cur:
tmp = cur.next
cur.next = Node(cur.val)
cur.next.next = tmp
cur = tmp
cur = head
while cur:
tmp = cur.next.next
cur.next.random = cur.random.next if cur.random else None #老节点指向空,新节点也指向空
cur = tmp
res = head.next
cur = head
while cur: #cur不为空,那么可以保证cur.next不为空,因为复制了一个
tmp = cur.next.next #三个while循环都是用cur,下一个也是指向cur
cur.next.next = cur.next.next.next if cur.next.next else None
cur = tmp
return res
4. 两个链表的第一个公共节点
方法一:哈希
class Solution:
def FindFirstCommonNode(self , pHead1 , pHead2 ):
dic = set()
while pHead1:
dic.add(pHead1)
pHead1 = pHead1.next
while pHead2:
if pHead2 in dic:
return pHead2
pHead2 = pHead2.next
方法二:双指针
时间复杂度降为O(1)
class Solution:
def FindFirstCommonNode(self , pHead1 , pHead2 ):
p1 = pHead1
p2 = pHead2
while p1 != p2: #找到第一个相等的节点则返回
p1 = p1.next if p1 else pHead2
p2 = p2.next if p2 else pHead1
return p1
5. 无环链表判相交
方法一:上一题双指针的方法
class CheckIntersect:
def chkIntersect(self, headA, headB):
# write code here
p1 = headA
p2 = headB
while p1 != p2:
p1 = p1.next if p1 else headB
p2 = p2.next if p2 else headA
return True if p1 else False
方法二:遍历到最后一个节点,看是否相同
class CheckIntersect:
def chkIntersect(self, headA, headB):
# write code here
while headA.next:
headA = headA.next
while headB.next:
headB = headB.next
return True if headA == headB else False