一、题目列表
- 寻找数组中出现次数超过一半的元素—>寻找数组中出现次数超过1/3的元素
- 股票的买入卖出时间,一次买入卖出—>多次买入卖出
- 寻找数组中的最长递增子序列,寻找最长长度—>输出最长递增子序列
- 翻转链表
- 画建最小堆的过程
- 先序遍历二叉树,非递归
- 链表的第一个公共节点,优化问题
- 最大连续子数组和
- 后缀子串
- 手写数组旋转、不用额外的空间
二、 解题思路
2.1 寻找数组中出现次数超过一半的元素—>寻找数组中出现次数超过1/3的元素
(leetcode169,229)
hashmap
因为我主要使用的是Python,这里对hashmap的理解大概是使用字典存储每个元素对应的次数进行求解
摩尔投票
针对1/k的通用情况,这样的画,数组中最多有k-1个元素是满足要求的,所以可以维护2个长度为k-1的列表,一个为candidate,用来记录候选的数字,另一个为nums,用于记录候选数字对应的出现次数。遍历数组中的元素,记当前数字为 e,如果e==candidate[i], 则nums[i]+1,否则,如果nums[i] == 0,那么candidate[i]=e, nums[i]=1,否则,对nums中的每一个元素num-1。最后再统计一下candidate中的每个元素出现的次数,将满足条件的的元素加入返回的结果中。
2.2 股票的买入卖出时间,一次买入卖出—>多次买入卖出
(leetcode121,122,123,188)
2.2.1一次买入卖出
计算相邻股票价格的差值,将问题转化为求数组中最大连续子数组和的问题
其原理:
2.2.2多次买入卖出,不限次数
计算相邻股票价格的差值,计算差值大于0的元素的和,即为最大利润
2.2.3两次买入卖出
计算相邻股票价格的差值
遍历i
dp1[i]表示从左到右第i位的最大利润
dp2[i]表示从右到左第i位的最大利润
2.2.4最多k次买入卖出
定义两个变量:
globalmax[i][j]:到第j天最多进行i次交易所获得的最大利润
localmax[i][j]:到第j天最多进行i次交易所获得的最大利润,且最后一次交易在第j天卖出
递推公式:
在上式中:
:
在j-1天中最多进行i-1次交易所获得的最大收益,以及在第j-1天买入第j天卖出的收益
:
在j-1天中最多进行i-1次交易所获得的最大收益,以及在第j天买入第j天卖出的收益,该收益为0
:
在第j-1天最多进行了i次交易,取消第j-1天的卖出行为,改为第j天卖出的收益
全局最大值的递推公式:
在上式中,在第j天共有2中选择,卖出与不卖出
2.3寻找数组中的最长递增子序列,寻找最长长度—>输出最长递增子序列
(leetocode300)
2.3.1寻找最长长度
动态规划:
dp[i]表示到第i个元素为止,最长递增子序列的长度
初始化,dp[i]=1
遍历i,将新元素nums[i]与i之前的所有元素进行比较,如果nums[i]>nums[j]且dp[i]
2.3.2输出最长递增子序列
在2.3.1思路的基础上,只要再维护一个数组,保存第i个数的上一个列内数的位置即可,然后根据最大长度最后一个元素所在的位置往前推即可得到递增子序列
2.4翻转链表
(leetcode206/92)
2.4.1全部翻转
以0—>1—>2—>3为例
初始化,newhead=None
1. p=head:p=0—>1—>2—>3,head=head.next:head=1—>2—>3
2. p.next=newhead:p=0—>None
3. newhead=p:newhead=0—>None
循环直到head为空
2.4.2部分翻转(位置m到n进行翻转)
先创建一个新的链表,对前m-1个位置不变,到达第m个位置时进行翻转,翻转方法与上述类似
2.5画建最小堆的过程
(leetcode155,225,232)
leetcode这几道题都比较简单,使用栈就可以实现
2. 6先序、中序、后续遍历二叉树,非递归/递归
(leetcode144)
定义二叉树结构如下:
# Definition for a binary tree node.
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
2.6.1先序遍历:
访问顺序:根左右
递归写法:
def preOrder(self,root):
if root == None:
return
print(root.val)
self.preOrder(root.left)
self.preOrder(root.right)
非递归写法:每次将遇到的节点压入栈,当左子树遍历完毕后才从栈中弹出最后一个访问的节点,再访问右子树
def preOrder(self,root):
if root == None:
return
stack = []
node = root
while node or stack:
while node:
print(node.val)
stack.append(node)
node = node.left
node = stack.pop()
node = node.right
2.6.2中序遍历
访问顺序:左根右
递归写法:
def inOrder(self,root):
if root == None:
return
self.inOrder(root.left)
print(root.val)
self.inOrder(root.right)
非递归写法:
def preOrder(self,root):
if root == None:
return
stack = []
node = root
while node or stack:
while node:
stack.append(node)
node = node.left
node = stack.pop()
print(node.val)
node = node.right
2.6.3后序遍历
访问顺序:左右根
递归写法:
def preOrder(self,root):
if root == None:
return
self.preOrder(root.left)
self.preOrder(root.right)
print(root.val)
非递归写法:使用两个栈对节点进行存储,stack1用于遍历,stack2用于存放输出的节点
def preOrder(self,root):
if root == None:
return
stack1 = []
stack2 = []
node = root
stack1.append(node)
while stack1:
node = stack1.pop()
if node.left:
stack1.append(node.left)
if node.right:
stack1.append(node.right)
stack2.append(node)
while stack2:
print(stack2.pop().val)
2. 7链表的第一个公共节点,优化问题
思路一:
两个有公共节点的链表是Y字型的结构,自公共节点后是重合的。可以借助两个栈先存储链表的节点。然后两个栈每次出一个节点,如果是重合节点,那么这两个节点是相等的。所以最后一个相等的节点就是第一个公共节点
思路二:
先遍历两个链表,统计长度a和b,让长链先走|a-b|步,公共节点肯定是存在于后面部分中,然后再对两个链表遍历匹配,遇到的第一个相同的节点就是公共节点
2. 8最大连续子数组和
定义2个变量,一个用来存储之前的累加和,一个用来存储当前的最大和
遍历数组中的每个元素,对于第i个数,如果前面的累加值为负或者0(前一个元素一定为负),则对累加值清零,将元素i的值赋给累加和,否则,继续累加,对累加和进行判断,如果大于最大和,则最大和更新,否则不更新。
2.9后缀子串
暂时不知道题目的具体意思,考虑python中endwith()函数进行判断
2. 10手写数组旋转、不用额外的空间
class Solution(object):
def rotate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: void Do not return anything, modify nums in-place instead.
"""
size = len(nums)
nums[:] = nums[size-k:] + nums[:size-k]