数据结构与算法

1、***两数之和为指定值
  • 给定一个整形数组,是否能找出其中的两个数使得其和为某个指定的值?

  • 示例: 输入数组为{1, 5, 7, 3}, 指定值为10, 则我们可以从中找出两个数3和7, 和等于10。

  • # 函数调用格式如下
    # -*- coding: UTF-8 -*-
    
    def hasSum(array, target):
        sorted_array = sorted(array)
        length = len(sorted_array)
        i, j = (0, length - 1)
        while i < j:
            if sorted_array[i] + sorted_array[j] > target:
                j -= 1
            elif sorted_array[i] + sorted_array[j] < target:
                i += 1
            else:
            	return sorted_array[i], sorted_array[j]
        
    if __name__ == '__main__':
        array = [1, 5, 3, 7]
        target = 4
        res = hassum(array, target)
        print(res)
    
  • 上面的方法只能找到一对和为目标值的数值,针对可能出现多对的情况

def hassum(array, target);
	sorted_array = sorted(array) # 默认为升序
    i, j = 0, len(array) - 1
    result = []
    while i < j:
        if sorted_array[i] + sorted_array[j] > target:
            j -= 1
        elif sorted_array[i] + sorted_array[j] < target:
            i += 1
        else:
            result.append((sorted_array[i], sorted_array[j]))
            i += 1
            
        
    return list(set(result))  # 去重
if __name__ == "__main__":
    array = [0, 0, 4, 1, 2, 3, 5, 7]
    target = 7
    res = hassum(array, target)
    print(res)
            
2、***股票买卖

给定一个数组,第i个元素代表第i天的股价。假设最多允许进行1次买卖,求可能的最大利润是多少?

  • 示例: 输入price = [12, 15, 14, 8, 11, 10, 12], 则输出最大利润是4。

  • def get_max_profit(price):
        if price is None or len(price) == 0:
            return 0
    
        max_profit = 0
        min_price = price[0]
        length = len(price)
        # 遍历选出最小价格和计算最大的差价
        for i in range(1, length):
            min_price = min(min_price, price[i])
            max_profit = max(max_profit, price[i] - min_price)
    
        return max_profit
    
    # 函数调用格式如下
    if __name__ == '__main__':
        price = [12, 15, 14, 8, 11, 10, 12]
        result = get_max_profit(price)
        print(result)
    
    
3、***快速排序
  • def quick_sort(array, start, end):
        # 递归条件
        if start >= end:
            return
    
        left, right = start, end
        pivot = array[start]
    
        while left < right:
            while array[right] >= pivot and left < right:
                right -= 1
            array[left] = array[right]
    
            while array[left] <= pivot and left < right:
                left += 1
            array[right] = array[left]
            
    	# 确认基准所在的位置,数组左侧都小于基准,右侧都大于基准
        array[left] = pivot
    
        quick_sort(array, start, left - 1)
        quick_sort(array, right + 1, end)
    
    
    if __name__ == '__main__':
        array = [4, 5, 5, 3, 3, 1, 6, 8, 7, 2]
        quick_sort(array, 0, len(array) -1)
        print(array)
    
4、蛙跳

蛙跳,给出一维非负元素的数组,每个元素代表该元素位置能够跳的最远距离。假设初始位置在第一个元素,请根据输入数组判断是否能跳到数组的末尾。

  • 示例:

    • 输入数组 A = [2, 1, 3, 1, 1], 输出True
    • 输入数组 A = [3, 2, 1, 0, 1], 输出False
  • A = [6, 2, 1, 1, 2, 1, 1, 0, 1]

  • # 思路:双指针
    # 同向移动的俩指针:第一个指针扫描当前值,第二个指针记录当前最远的目的地。
    # 扫描的同时,实时更新当前最远目的地。如果当前最远目的地能到达数组尾端,程序返
    # 回true。如果当前最远目的地不能到达尾端,而且又不能向前移动的时候,我们认为不
    # 可能到达终点,程序返回false。
    
    def frog_jump(arr):
        # 数组长度
        length = len(arr)
        # 如果数组长度小于等于1,则返回False
        if length <= 1:
            return False
    
        # 设置双指针,一个记录当前位置值,一个记录当前能跳最远距离
        curMax = 0
        for i in range(length - 1):
            # 如果当前位置值为零,当前最远距离无法后移
            # 则认为蛙无法继续向前跳
            if arr[i] == 0 and curMax < i + 1:
                return False
    
            # 如果当前位置元素非零,且当前最远距离大于之前最远距离
            # 则认为蛙可以 继续向后跳
            if arr[i] > 0 and arr[i] + i > curMax:
                curMax = arr[i] + i
    
                # 当最远位置大于等于数组长度时,认为蛙可以跳到末尾
                if curMax >= length - 1:
                    return True
        # 如果上述条件都不满足,则返回False
        return False
    
    if __name__ == '__main__':
        a = [2, 1, 3, 1, 1]
        b = [3, 2, 1, 0, 1]
        res = frog_jump(b)
        print(res)
        res = frog_jump(a)
        print(res)
    
5、数字分水岭

在一维数组中, 找出一个点, 使得其所有左边的数字均小于等于它,所有右边的数字都大于等于它, 返回这个点所在的下标。
思路:建立False矩阵,用于记录比左侧数值都大的位置,然后左遍历寻找比左侧都大的数值,右遍历比右侧数值都小的数,然后通过false矩阵确定最终位置。

  • 示例: 输入一维数组A = [1, 0, 1, 0, 1, 2, 3], 返回下标4或5。

    def get_point_number(arr):
        arrLen = len(arr)
        if arr == None or arrLen == 0:
            return False
    	#用于记录左遍历时比左侧数都大的值的位置
        isCurMax = [False for i in range(arrLen)]
    
        # 第一个指针:左遍历最大值
        leftMax = arr[0]
        for i in range(arrLen - 1):
            if arr[i] >= leftMax:
                leftMax = arr[i]
                # 如果比之前最大值还大,则认为大于左侧所有数
                isCurrMax[i] = True
    
        res = []
    
        # 第二个指针记录从右侧遍历比右侧都小的值
        rightMin = arr[arrLen - 1]
        for i in range(arrLen - 1, -1, -1):
            if arr[i] <= rightMin:
                rightMin = arr[i]
    			# 数值满足比其右侧都小,且比其左侧都大
                if isCurrMax[i]:
                    res.append(i)
                    
        return sorted(res) if len(res) > 0 else False
    
    
    if __name__ == '__main__':
        a = [1, 0, 1, 0, 1, 2, 3]
        res = get_point_number(a)
        print(res)
    
6、找出仅出现一次的数值

给定一个一维整形数组,里面只有一个数字出现了一次,其他数字都出现了2次,请将这个唯一出现了一次的数字找出来。

  • 示例: 给定数组 A = [2, 35, 8, 16, 8, 2, 7, 16, 35], 返回唯一只出现了一次的7

  • 异或运算性质:

    • 任何数和 0做异或运算,结果仍然是原来的数,即 a ⊕ \oplus 0=a。

    • 任何数和其自身做异或运算,结果是 0,即 a ⊕ \oplus a=0。

    • 异或运算满足交换律和结合律,即 a ⊕ \oplus b ⊕ \oplus a=b ⊕ \oplus a ⊕ \oplus a=b ⊕ \oplus (a ⊕ \oplus a)=b⊕0=b。

  • def onlyOneTimeNumber(arr):
        arrLen = len(arr)
        if arrLen < 2:
            return -1
        res = 0
        for i in range(arrLen):
            # 异或表达式
            res ^= arr[i]
    
        return res
    
    # 函数调用格式如下
    if __name__ == '__main__':
        array = [2, 35, 8, 16, 8, 2, 7, 16, 35]
        num = onlyOneTimeNumber(array)
        print("num=%d," % num)
    
    
# from functools import reduce
# def singlenum(array):
#     return reduce(lambda x, y: x ^ y, array)
7、求一个二叉树的最大深度。
  • # 类的定义如下
    class TreeNode(object):
        def __init__(self, x):
            self.val = x
            self.left = None
            self.right = None
    
    
    def maxDepth(root):
            
        if root is None:
            return 0
        l = maxDepth(root.left)
        r = maxDepth(root.right)
            
        return max(l, r) + 1
    
    # 函数调用格式如下
    if __name__ == '__main__':
        root = TreeNode(5)
        root.left = TreeNode(3)
        root.right = TreeNode(4)
        root.right.right = TreeNode(0)
        res = maxDepth(root)
        print(res)
    
  • 求二叉树的最小深度

class Solution:
    def minDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        
        if not root.left and not root.right:
            return 1
        
        min_depth = 10**9
        if root.left:
            min_depth = min(self.minDepth(root.left), min_depth)
        if root.right:
            min_depth = min(self.minDepth(root.right), min_depth)
        
        return min_depth + 1
8、***最长回文子串

最长回文子串, 给定一个字符串s, 找到s中最长的回文子串, 假设s的最大长度为1000.

  • 示例: s = “helloollyyd”, 则最长回文子串是"llooll", 长度为6

  • 思路

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSgsvJad-1628333885058)(.\image\最长回文子串思路.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-27WF6C57-1628333885062)(.\image\最长回文子串动态规划矩阵.png)]

    • def longestPalindrome(s):
          n = len(s)
          if n < 2:
              return s
      
          max_len = 1
          begin = 0
          # dp[i][j] 表示 s[i..j] 是否是回文串
          dp = [[False] * n for _ in range(n)]
          for i in range(n):
              dp[i][i] = True
      
          # 递推开始
          # 先枚举子串长度
          for L in range(2, n + 1):
              # 枚举左边界,左边界的上限设置可以宽松一些
              for i in range(n):
                  # 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                  j = L + i - 1
                  # 如果右边界越界,就可以退出当前循环
                  if j >= n:
                      break
      
                  if s[i] != s[j]:
                      dp[i][j] = False
                  else:
                      if j - i < 3:
                          dp[i][j] = True
                      else:
                          dp[i][j] = dp[i + 1][j - 1]
      
                  # 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
                  if dp[i][j] and j - i + 1 > max_len:
                      max_len = j - i + 1
                      begin = i
          return s[begin:begin + max_len]
      
      # 函数调用格式如下
      if __name__ == '__main__':
          s = "helloollyyd"
          res = longestPalindrome(s)
          print("res=," res)
      
      

9、保持顺序,移动0至末尾
  • 给定一个数组nums, 编写一个函数将所有0移动到数组末尾, 同时保持非零元素的相对顺序不变。

    • 示例: 输入数组nums = [0, 1, 0, 3, 12], 输出结果: [1, 3, 12, 0, 0]
    • 思路及解法

    使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。

    右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。

    注意到以下性质:

    左指针左边均为非零数;

    右指针左边直到左指针处均为零。

    复杂度:O(n)

    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            n = len(nums)
            left = right = 0
            while right < n:
                if nums[right] != 0:
                    nums[left], nums[right] = nums[right], nums[left]
                    left += 1
                right += 1
    
    • def move_array(nums):
          for num in nums:
              if num == 0:
                  nums.remove(num)
                  nums.append(num)
        return nums
      
      if __name__ == '__main__':
      	nums = [0, 1, 0, 3, 12]
      	res = move_array(nums)
      	print(res)
      
    10、蛙跳2.0

    在之前作业的基础上实现蛙跳2.0,给出一维非负元素的数组,每个元素代表该元素位置能够跳的最远距离。假设初始位置在第一个元素,并假设输入数组能满足到达数组末尾,求最少的跳数是多少?

    • 示例: 输入数组 A = [2, 1, 3, 1, 1], 输出2

    • def main():
          a = [2, 1, 3, 1, 1]
      
          res = frog_jump(a)
          print(res)
      
      
      def frog_jump(arr):
          result = 0
          # 使用俩指针,分别记录上一轮和本轮的最远目的地
          last = 0
          curr = 0
          for i in range(len(arr)):
              if i > last:
                  # 如果当前指针越过了上轮最远目的地
                  # 那么更新上轮指针,并且实施一跳
                  last = curr
                  result += 1
              # 保存本轮的最远目的地值
              curr = max(curr, i + arr[i])
          return result
      
      
      if __name__ == '__main__':
          main()
      
      
      # 函数调用格式如下
      def main():
          a = [2, 1, 3, 1, 1]
          res = frog_jump(a)
          print(res)
      
      if __name__ == '__main__':
          main()
      
11、机器人路径
  • 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为"Start")

  • 机器人每次只能向下或者向右移动一步, 机器人试图达到网格的右下角(在下图中标记为"Finish")

  • 问总共有多少条不同的路径?

  • 示例1:
    输入: m = 3, n = 2
    输出: 3
    解释:
    从左上角开始,总共有 3 条路径可以到达右下角。
    1. 向右 -> 向右 -> 向下
    2. 向右 -> 向下 -> 向右
    3. 向下 -> 向右 -> 向右
    
    示例2:
    输入: m = 7, n = 3
    输出: 28
    
    注意: 1 <= m, n <= 100
    
  • # 函数调用格式如下
    def main():
        number = allPaths(m=3, n=2)
        print("number=%d," % number)
    
    
    if __name__ == '__main__':
        main()
    
# 函数调用格式如下
def allPaths(m, n):
    # 动态规化
    matrix = [[0 for i in range(n)] for i in range(m)]

    """
    matrix[0][0] = 1
    matrix[1][n] = 1
    matrix[n][1] = 1
    """
    matrix[0][0] = 1

    for i in range(1, m):
        for j in range(1, n):
            if i == 1:
                matrix[0][j] = 1

            if j == 1:
                matrix[i][0] = 1

            matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1]

    return matrix[m - 1][n - 1]


def main():
    number = allPaths(m=7, n=3)
    print("number=%d," % number)


if __name__ == '__main__':
    main()

12、最大下标距离

给定一个整形数组, 找出最大下标距离 j - i , 当且仅当 A[i] < A[j] 和 i < j 。

  • 示例: 输入数组为[5, 3, 4, 0, 1, 4, 1], 则最大下标距离产生于A[i]=3, i=1和A[j]=4, j=5的时候, 此时j - i = 4, 这个4就是满足条件的最大下标距离。

  • # 函数调用格式如下
    def main():
        array = [5, 3, 4, 0 ,1, 4, 1]
        dis = maxDistance(array)
        print("dis=%d," % dis)
    
    
    if __name__ == '__main__':
        main()
    
13、***爬楼梯

假设你正在爬楼梯, 需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶. 你有多少种不同的方法可以爬到楼顶呢?

  • **注意:**给定 n 是一个正整数.

  • 示例1:
    输入: 2
    输出: 2
    解释: 有两种方法可以爬到楼顶。
    1.  1 阶 + 1 阶
    2.  2 阶
    
    示例2:
    输入: 3
    输出: 3
    解释: 有三种方法可以爬到楼顶。
    1.  1 阶 + 1 阶 + 1 阶
    2.  1 阶 + 2 阶
    3.  2 阶 + 1 阶
    
'''
标签:动态规划
本问题其实常规解法可以分成多个子问题,爬第n阶楼梯的方法数量,等于 2 部分之和
倒数第二步爬上 n-1 阶楼梯的方法数量。因为再爬1阶就能到第n阶
倒数第二步爬上 n-2 阶楼梯的方法数量,因为再爬2阶就能到第n阶
所以我们得到公式 arr[n] = arr[n-1] + arr[n-2]
同时需要初始化 arr[1]=1 和 arr[2]=2
时间复杂度:O(n)

'''

def methods(n):
    if n == 1:
        return '输入 %d 不符合题意' % n

    arr = [0 for i in range(n + 1)]

    i = 0
    while i <= n:
        if i == 0 or i == 1:
            arr[i] = 1
            i += 1
            continue

        arr[i] = arr[i - 1] + arr[i - 2]

        i += 1

    # print(arr)
    return arr[n]


def main():
    variable = methods(n=3)
    print("dis=%d," % variable)


if __name__ == '__main__':
    main()

14、***最长公共子序列

给定两个字符串S1、S2,求两个字符串的最长公共子序列

  • 递归方法

    • 因为求最长,所以从字符串末尾进行比较
    • 如果末尾相同,则保留
      • 再从倒数第二位开始比较
    • 如果不同,分两种情况
      • 删除S1的末尾元素,用倒数第二位同S2的末尾元素,进行比较
      • 删除S2的末尾元素,用倒数第二位同S1的末尾元素,进行比较
    • 重复上面步骤
    def LCS(s1, s2):
        # 判断是否位空
        if s1 == '' or s2 == '':
            return ''
        # 比较末位元素
        elif s1[-1] == s2[-1]:
            return LCS(s1[:-1],s2[:-1]) + s1[-1]
            
        else:
            # 如果不相等,分两种情况
            res_a = LCS(s1[:-1], s2)
            res_b = LCS(s1, s2[:-1])
            
            if len(res_a) > len(res_b):
                return res_a
            return res_b
        
    if __name__ == "__main__":
        a = 'abcd'
        b = 'aebd'
        print(LCS(a, b))
    
  • 动态规划

    • L
15、二叉树锯齿形层次遍历(Z字型遍历算法)
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
        
class Solution(object):
    def zigzaglevelorder(self, root):
        res = []
        que = [root]
        if root == None:
            return res
        while que:
            tempList = []
            for i in range(len(que)):
                node = que.pop(0)
                tempList.append(node.val)
                
                if node.left:
                    que.append(node.left)
                if node.right:
                    que.append(node.right)
            res.append(tempList)
            
        temp = []
        for i in range(len(que)):
            if i % 2 == 0:
                temp.append(res[i])
            else:
                temp.append(res[i][::-1])
        return (temp)
16、判断压栈弹出顺序

输入两个整数序列,第一个序列表示站的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列(注意:这两个序列的长度是相等的)

  • 栈特点:先进后出–栈顶元素先出
  • 思路:
    • 借用一个辅助栈,遍历压栈顺序,先将第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈。出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。
def validateStackSequences(self, pushed, popped):
        stack, i = [], 0
        for num in pushed:
            stack.append(num) # num 入栈
            while stack and stack[-1] == popped[i]: # 循环判断与出栈
                stack.pop()
                i += 1
        # stack 为空则返回True,非空则为false
        return not stack

if __name__ == '__main__':
    pushed = [1,2,3,4,5]
    poped = [4,3,5,1,2]
    res = validateStackSequences(pushed, poped)
    print(res)
17、字符串转换成树结构

输入是一个字符串形式表达的树,需要把它转换成一个真正的树结构。

例如:	   a
#	 b		  c
# d     e  f			<---"(a(b(de)c(f)))"
18、在一个乱序的数组中找第K大的数字

19、反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点

class ListNode:
     """链表数据结构"""
     def __init__(self, x, next=None):
         self.val = x
         self.next = next

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        # 定义尾部节点,默认为None
        last = None
        # 当传入的头部节点不为None时,即没有反转完成时
        while head:
            # 连续赋值,等效于:将等号右边的变量全部暂存为中间变量, 再赋值给右侧,顺序从左至右
            # eg: l = last, h = head, hn = head.next, head.next = l, last = h, head = hn
            # 按照反转链表的定义,反转后新链表的最后一个节点是当前的head节点,所以对于新链表head.next=None(last的默认值)
            # 而last是一直变动的,处理完新链表的尾部后,从head开始,所以有last=head
            # last的遍历过程在改变着链表,我们是使用head来限制其应该何时结束,因此head也应该逐次变化,直到head.next指向None
            # 因此head变化为:head = head.next
            head.next, last, head = last, head, head.next
        return last
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值