概述
冒泡排序
冒泡排序:
这个算法的核心之处就是在每个相邻的元素都进行比较,如果不是按顺序的形式,就彼此交换。总共要循环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
最后
以上就是伶俐心锁为你收集整理的冒泡排序,快速排序,堆排序,滑动窗口,双指针的全部内容,希望文章能够帮你解决冒泡排序,快速排序,堆排序,滑动窗口,双指针所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复