练习题 Day6

剑指 Offer 50. 第一个只出现一次的字符

在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

s = "abaccdeff"
返回 "b"

class Solution:
    def firstUniqChar(self, s):
        lists = list(s)
        d = dict()
        
        for ss in lists:
             d[ss] = not ss in d  # 没出现过=1,出现过=0
        
        find = False
        for key in d.keys():
            if d[key]: # 没出现过=1
                find = True
                break
        
        if find:
            return key
        if not find:
            return ' '

剑指 Offer 52. 两个链表的第一个公共节点

交替遍历!!!这段code 我直接好家伙!!

巧妙的消除两个链表的长度差

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    
    def getIntersectionNode(self, headA, headB):
        node1, node2 = headA, headB
        
        while node1 != node2:
            node1 = node1.next if node1 else headB
            # 当 node1 到达链表 headA 的末尾时,重新定位到链表 headB 的头结点
            node2 = node2.next if node2 else headA
            # 当 node2 到达链表 headB 的末尾时,重新定位到链表 headA 的头结点.

        return node1

剑指 Offer 53 - I. 在排序数组中查找数字 I

统计一个数字在排序数组中出现的次数。

nums = [5,7,7,8,8,10]
target = 8
count = 0
for num in nums:
    if num == target:
        count += 1
    if num > target:
        break
print(count)

剑指 Offer 53 - II. 0~n-1中缺失的数字

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

输入: [0,1,2,3,4,5,6,7,9] 输出: 8
输入:[1,2] 输出0
输入:[0,1] 输出2

class Solution:
    def missingNumber(self, nums):
        '''
        解法一
        for _ in range(len(nums)+1):
            if _ not in nums:
                return _
        '''
        # 解法二(数学逻辑)
        n = len(nums)
        total = n * (n + 1) // 2
        return total - sum(nums)      

剑指 Offer 54. 二叉搜索树的第k大节点

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

# 定义测试用例
t1 = TreeNode(5)
t2 = TreeNode(3)
t3 = TreeNode(9)
t1.left = t2; t1.right = t3
t4 = TreeNode(1)
t5 = TreeNode(4)
t6 = TreeNode(18)
t2.left = t4; t2.right = t5; t3.right = t6

二叉搜索树的遍历问题

参考链接题解https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/tu-jie-er-cha-shu-de-si-chong-bian-li-by-z1m/

首先搞清楚遍历规则:

  • 前序:5 3 1 4 9 18
  • 中序 1 3 4 5 9 18
  • 后序 1 4 3 18 9 5
  • 层次 5 3 9 1 4 18

递归解法

# 前序
def qianxu(root):
    res = []
    def dfs(root):
        nonlocal res
        if not root:
            return
        else:
            res.append(root.val)   # 变动位置
            dfs(root.left)
            dfs(root.right)
    dfs(root)
    return res
print(qianxu(t1))

# 中序
def zhongxu(root):
    res = []
    def dfs(root):
        nonlocal res
        if not root:
            return
        else:
            dfs(root.left)
            res.append(root.val)  # 变动位置
            dfs(root.right)
    dfs(root)
    return res
print(zhongxu(t1))

# 后序
def houxu(root):
    res = []
    def dfs(root):
        nonlocal res
        if not root:
            return
        else:
            dfs(root.left)
            dfs(root.right)
            res.append(root.val)  # 变动位置
    dfs(root)
    return res
print(houxu(t1))

迭代解法

  • 层次遍历——队列,左子节点先进先出
  • 前序遍历——1.栈,右子节点先进后出,之后append;2.栈,左子节点一边先进一边,再轮到右子节点
  • 中序遍历——栈,左子节点先进,全进之后append
  • 后序遍历——1.前序遍历左右互换,最后反向输出;2.用一个flag 让根节点保持在队伍前方,从而晚于子节点输出。
# 层次——队列迭代
def cengci(root):
    res = []
    if not root:
        return res
    nodelist = [root]
    while nodelist:
        cur = nodelist.pop(0)
        res.append(cur.val)
        if cur.left:
            nodelist.append(cur.left)
        if cur.right:
            nodelist.append(cur.right)
    return res
print(cengci(t1))


# 前序遍历迭代第一种
def qianxu2(root):
    res = []
    if not root:
        return res
    nodelist = [root]
    while nodelist:
        # nodelist右子节点先进——栈pop左子节点先出
        cur = nodelist.pop() 
        res.append(cur.val)
        if cur.right:
            nodelist.append(cur.right)
        if cur.left:
            nodelist.append(cur.left)
    return res
print(qianxu2(t1))

# 前序遍历迭代第二种
def qianxu2(root):
    res = []
    if not root:
        return res
    nodelist = []
    cur = root
    while nodelist or cur:
        while cur:
            nodelist.append(cur)
            res.append(cur.val)
            cur = cur.left
        cur = nodelist.pop()
        cur = cur.right
    return res
print(qianxu2(t1))


# 中序遍历
def zhongxu2(root):
    res = []
    if not root:
        return res
    cur = root
    nodelist = []
    while nodelist or cur:
        while cur: 
            nodelist.append(cur)
            cur = cur.left
        cur = nodelist.pop()
        res.append(cur.val)
        cur = cur.right
    return res
print(zhongxu2(t1))

# 后序遍历第一种——把前序左右反过来,然后倒序输出
def houxu2(root):
    res = []
    if not root:
        return res
    nodelist = []
    cur = root
    while nodelist or cur:
        while cur:
            nodelist.append(cur)
            res.append(cur.val)
            cur = cur.right
        tmp = nodelist.pop()
        cur = tmp.left
    return res[::-1]
print(houxu2(t1))

# 后序遍历第二种——栈操作,前序根-左-右,后续左-右-根,
# 如果是根节点先append回去最后pop
def houxu3(root):
    res = []
    if not root:
        return res
    nodelist = [(0, root)]
    while nodelist:
        flag, cur = nodelist.pop()
        if flag == 1:  # 如果是1
            res.append(cur.val)
        else:  # 如果不是重新append回去
            nodelist.append((1, cur))
            if cur.right:
                nodelist.append((0, cur.right))
            if cur.left:
                nodelist.append((0, cur.left))
    return res

print(houxu3(t1))

剑指 Offer 55 - II. 平衡二叉树

如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def maxDepth(root):
            if not root:
                return 0
            return max(maxDepth(root.left),maxDepth(root.right))+1
        if not root:
            return True
        diff = abs(maxDepth(root.left) - maxDepth(root.right))
        if diff > 1:
            return False
        else:
            return self.isBalanced(root.left) and self.isBalanced(root.right) 
s7 = Solution()
s7.isBalanced(t1)

剑指 Offer 56 数组中出现数字的次数

——字典通解

#在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
def findUnique(nums):
    d = dict()
    res = []
    for num in nums:
        d[num] = 0 if num in d else 1
    for num in d:
        if d[num]:
            res.append(num)
    return res
findUnique([9,1,7,9,7,9,7]), findUnique([1,2,10,4,1,4,3,3])

剑指 Offer 57. 和为某个数字

1.找出和为X的两个数字

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

  • 开始的二重for暴力超时了!
  • 要充分利用这个数组递增的性质,提高运算效率
class Solution:
    def twoSum(self, nums, target):
        # 使用双指针
        n = len(nums)
        left, right = 0, n-1
        while left < right:
            tmpsum = nums[left] + nums[right] 
            if tmpsum == target:
                return [nums[left], nums[right]]
            if tmpsum > target:
                right -= 1
            if tmpsum < target:
                left += 1

找出所有和为s的连续正数序列

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

  1. 方法一:循环/滑动窗口
    target = 9
    12345如果有一颗等于则输出,一旦大于就break,下一次从2开始
  2. 方法二:利用求和公式找到i,从而减少一次循环次数——时间复杂度大大降低
### 1.方法1  二重循环
target = 15
res = []
for i in range(1, target//2 + 1):  # 至少含有两个数字
    tmpsum = i
    tmp_res = [i]
    for j in range(i+1, target):
        tmpsum += j
        tmp_res.append(j)
        if tmpsum == target:
            break
        if tmpsum > target:
            tmp_res =  []
            break
    if tmp_res:
        res.append(tmp_res)
print(res)

### 2.方法2  滑动窗口
i, j = 1, 2
res2 = []
while j <= target//2 + 1 and i < j:
    cur_sum = sum(list(range(i, j+1)))  # 当前和
    if cur_sum < target:
        j +=1
    if cur_sum > target:
        i +=1
    if cur_sum == target:
        res2.append(list(range(i, j+1)))
        i += 1  # 进行下一种情况
print(res2)

### 3.运用数列求和公式找到可能的i,从而减少一次循环次数
res3 = []
for k in range(1, target//2):
    i = ((2 * target) / (k+1) - k) / 2
    if i >0 and i%1==0:
        res3.append(list(range(int(i), int(i)+k+1)))
print(res3[::-1])

剑指 Offer 58 - I. 翻转单词顺序

输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。同时删掉多余的空格!

e.g.输入: " hello world! "
输出: "world! hello"

str = '   I  am a  a student.  !!!   '
strlist = list( str )
res = []
i = 0
while i < len( strlist ):
    tmp = []
    j = 0
    # 每一个单词作为一个[] 整体
    while ( i + j < len(strlist) ) and ( strlist[ i + j] != ' ' ):
        tmp.append(strlist[ i + j ])
        j += 1
    # 对下一个字符进行操作
    i += j + 1
    if tmp:
        res.append(tmp)
# 结果——一个嵌套list
print(res)
 
# 用一个栈倒序输出
ress = []
while res:  
    ress.append(''.join(res.pop()))
print(' '.join(ress))
[['I'], ['a', 'm'], ['a'], ['a'], ['s', 't', 'u', 'd', 'e', 'n', 't', '.'], ['!', '!', '!']]
!!! student. a a am I

剑指 Offer 58 - II. 左旋转字符串

是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

s = "yangshuyi";n = 4
# 1.直接切片
print(s[n:] + s[:n])
# 2.队列问题
strlist = list(s)
for i in range(n):
    tmp = strlist.pop(0)
    strlist.append(tmp)
print(''.join(strlist))
shuyiyang
shuyiyang