冒泡排序,快速排序,堆排序,滑动窗口,双指针

冒泡排序

冒泡排序:
这个算法的核心之处就是在每个相邻的元素都进行比较,如果不是按顺序的形式,就彼此交换。总共要循环n-1次(因为是两两比较),第一次循环结束,最大后一位就确定下来是最大的数了。第二次循环就可以确定,倒数第二位是第二大的数。一直循环n-1次,就可以得到排列好的数组。
注意在每次循环的过程中,都要减去已经排好序的数字。
这个算法不适合用于大数据量的排序,因为它的平均时间复杂度和最差时间复杂度都是o(n^2)。
因为每一次都要循环n-1…n-2…n-2…0,所以时间复杂度为(0+n-1)*n/2,即为O(n ^ 2)。

n=len(arrList)
#外面的循环就是要控制循环的次数,每次循环结束都可以确定一个最大数的位置
for i in range(n-1):
	#里面的循环就是让数字两两作比较,这里减了一个1,所以下面不会越界
	for j in range(n-1-i)if arrList[j+1] < arrList[j]:
			arrList[j],arrList[j+1] = arrList[j+1],arrList[j]

快速排序

基本思想:
是冒泡排序的一种改进算法,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,最终使整个数据变成有序序列。
1.在数据集中选择一个元素作为基准
2.所有小于基准的移到基准左边,大于基准的移动到基准的右边。
3.对基准的左右两个子集,不断重复第一第二步,直到所有子集只剩下一个元素为止。

快速排序的平均时间复杂度是O(nlogn),最坏情况下时间复杂度为O(n ^ 2)。

挖坑法
把基准的地方挖坑掉,然后左右一个指针指向数组的头和尾(如果基准是第一个元素,则头指针指向第二个元素)。右指针上的数如果比基准小,则把右指针上的数填入坑中,然后右指针上就成一个新的坑,然后右指针向后移动一位。左指针上的数如果比基准大同理,如果比基准小,指针就向后移动一位。直到左右两个指针相遇,则停止,并把基准填入到相遇的这个坑。然后递归。

指针交换法
除基准点外,头尾左右指针(和上面挖坑法一样)。找到左指针中比基准大的数,和右指针上比基准小的数(如果指针上的数不满足,则指向向后或者向前移动一位),然后交换位置。直到左右指针相遇停止,如果右指针先移动则把基准插入到相遇后面,反之同理。

题目:
在未排序的数组中找到第k个最大元素。请注意,你需要找的是数组排序后的第k个最大的元素,而不是第k个不同的元素。(leetcode 215)

#填坑法
def partition(self,nums,left,right):
	pivot = nums[left]
	i , j = left , right
	while i < j:
		while i < j and nums[j] >= pivot:
			j -= 1
		if i < j:
			nums[i] = nums[j]
			i += 1
		while i < j and nums[i] <= pivot:
			i += 1
		if i < j:
			nums[j] = nums[i]
			j -= 1
	nums[i] = pivot
	return i
#快速排序
def findKthLargest(self,nums,k):
	#经过排序后这个索引的位置就是答案
	self._k = len(nums) - k
	return self.quicksort(nums,0,len(nums)-1)
def quicksort(self,nums,left,right):
	#递归终止条件就是排序后剩下一个元素,此时的数组也是排好序的了
	if left == right:
		return nums[left]
	#先让第一个元素找到排好序索引的位置
	pivot = self.partition(nums,left,right)
	if pivot == self._k:
		return nums[pivot]
	#如果排好序的索引比需要找的数的索引小,则在它基准点的右边进行寻找
	elif pivot < self.k:
		return self.quicksort(nums,pivot + 1,right)
	else:
		return self.quicksort(nums,left,pivot - 1)

堆排序

二叉堆
二叉堆本质上是一种完全二叉树,它分为两个类型:
1.最大堆:最大堆任何一个父节点的值,都大于等于它左右孩子节点的值。
2.最小堆:最小堆任何一个父节点的值,都小于等于它左右孩子节点的值。

二叉堆的根节点叫做堆顶。
最大堆和最小堆的特点,决定了在最大堆堆顶是整个堆中的最大元素最小堆堆顶是整个堆中的最小元素
最大堆实例:
在这里插入图片描述
堆的自我调节
看最后插入的那个元素,是否满足最小堆的定义,不满足则向上调整(在调整过程中也要看被调整的元素是否满足最小堆的定义),一直到满足定义或者调整到了根节点。
在这里插入图片描述
堆的代码实现
二叉堆是存储在数组当中的。所以可以很好的通过父节点找到它的左右节点,(left = parent * 2 + 1),(right = parent * 2 + 2)。也可以通过子节点找到父节点,(parent = (left or right - 1) // 2)。

滑动窗口

题目:
给定一个整形数组和一个数字s,找到数组中最短的一个连续子数组,使得连续子数组的数字和sum >= s,返回这个最短的连续子数组的长度。
如,给定数组[2,3,1,2,4,3],s=7。答案为[4,3],返回2。(leetcode 209)

思路:定义一个可以滑动的窗口在数组里面,如果滑动窗口里面的值小于s,则右边的指针向右移动一位(增大了里面的值)。如果滑动窗口里面的值大于s,则记录下滑动窗口里面的元素个数,并和上一次的记录比较(如果比上一次的个数少则替换),然后左边的指针也向后移动一位。直到右边的指针到达数组长度则停止。
两个指针left和right,left代表滑窗的左边框,right代表滑窗的右边框。开始时两个指针都为0。
在这里插入图片描述
代码:

def findMinList(self,nums,s):
	i , j = 0 , 0
	#初始一个滑动框的长度,最大长度就是数组的长度加1
	r = len(nums) + 1
	#用把数组内的每次新加一个数的和来求滑动窗口内数字的和,把图画出来就好理解了
	sums = []
	for num in nums:
		if not sums:
			sums.append(num)
		else:
			sums.append(sums[-1] + num)
		while i < len(nums) and j < len(nums):
			#求滑动窗口内的数字和,画图可以理解
			if sums[j] - sums[i] + nums[i] < s:
				j += 1
			else:
				#判断窗口内的长度会不会比上一个的短,短则记录
				if j - i + 1 < r:
					r = j - i + 1
				i += 1
		if r != len(nums) + 1:
			return r
		else:
			return 0

双指针

题目:
给定一个有序整型数组和一个整数target,在其中寻找两个元素,使其和为target。返回这两个数的索引。(leetcode 167)

对撞指针(相似的题:125,344,345,11*)
定义两个指针,分别在数组的头和尾。如果两个指针的数相加为target,则返回。如果大于target,则尾指针减1,。如果小于target,则头指针加1。当头指针大于尾指针的索引的时候,跳出循环。

def twoSum(self,numbers,target):
	i , j = 0 , len(numbers) - 1
	while i < j:
		if numbers[i] + numbers[j] == target:
			break
		elif numbers[i] + numbers[j] < target:
			i += 1
		else:
			j += 1
	return [i+1 , j+1]

快慢指针
题目:
给定一个链表,返回链表开始入环的第一个节点。如果链表无环,则返回null。(leetcode 146)

搞一个快指针(一次走两步),一个慢指针(一次走一步),当它们在这个环里面相遇的时候停止下来(如果快指针走到了null,则没有环)。然后慢指针继续走,并且再来一个慢指针从头开始走,直到它们相遇则为环的入口。

代码:

def detectCycle(self,head):
	fast , slow = head , head
	while fast != None:
		fast = fast.next
		if fast == None:
			break
		fast = fast.next
		slow = slow.next
		if slow == fast:
			break
	if fast == None:
		return None
	p1 , p2 = slow , head
	while p1 != p2:
		p1 = p1.next
		p2 = p2.next
	return p1
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值