1 两数之和
思路:使用一个HashMap集合存储每一个元素和对应下标。
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map=new HashMap<>();
for(int i=0;i<nums.length;i++){
//当前元素加上left后就等于目标值
int left=target-nums[i];
//如果left已经存在,说明存在这样的两个值,直接返回
if(map.containsKey(left)){
return new int[]{i,map.get(left)};
}else{
//不存在,就把当前元素和下标存入
map.put(nums[i],i);
}
}
return new int[2];
}
2 有效的括号
思路:使用HashMap和栈解决。
public boolean isValid(String s) {
HashMap<Character,Character> map=new HashMap<>();
map.put(')','(');
map.put('}','{');
map.put(']','[');
Stack<Character> stack=new Stack<>();
for(int i=0;i<s.length();i++){
//获取当前括号
char c=s.charAt(i);
//如果是右括号,判断栈顶是否为对应左括号
if(map.containsKey(c)){
if(!stack.isEmpty()&&stack.peek()==map.get(c)){
stack.pop();
}else //不匹配直接返回false
return false;
}else {//是左括号,存入栈
stack.push(c);
}
}
//如果栈空了,说明全部匹配成功
return stack.isEmpty();
}
3 合并两个有序链表
思路:while循环比较两个链表当前节点的值,将较小值节点作为新节点即可。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//新链表表头
ListNode pre=new ListNode(-1);
//遍历指针
ListNode node=pre;
while(l1!=null&&l2!=null){
if(l1.val<=l2.val){
node.next=l1;
l1=l1.next;
}else{
node.next=l2;
l2=l2.next;
}
node=node.next;
}
//防止l1和l2有一个还没遍历完
node.next=l2==null?l1:l2;
return pre.next;
}
4 最大子序和
思路:用一个临时遍历保持和,如果小于0就重新开始计算。
public int maxSubArray(int[] nums) {
if(nums.length==0)
return 0;
int max=nums[0];
int temp=nums[0];
for(int i=1;i<nums.length;i++){
if(temp<0)
temp=nums[i];
else
temp+=nums[i];
//更新最大值
if(temp>max)
max=temp;
}
return max;
}
5 爬楼梯
思路:经典的动态规划,比较简单,爬到i阶的方法=爬到i-1阶的方法+爬到i-2阶的方法。
public int climbStairs(int n) {
if(n==0||n==1||n==2)
return n;
int[] dp=new int[n+1];
dp[0]=0;
dp[1]=1;
dp[2]=2;
for(int i=3;i<n+1;i++){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
6 对称二叉树
思路:先判断根节点,如果不为空,再比较它的左右节点。
public boolean isSymmetric(TreeNode root) {
if(root==null)
return true;
return isMirror(root.left,root.right);
}
public boolean isMirror(TreeNode n1,TreeNode n2){
//都为空,对称
if(n1==null&&n2==null)
return true;
//一个为空,一个不为空,不对称
if((n1==null&&n2!=null)||(n2==null&&n1!=null))
return false;
//值不相等,不对称
if(n1.val!=n2.val)
return false;
//继续比较对称节点的关系
return isMirror(n1.left,n2.right)&&isMirror(n1.right,n2.left);
}
7 二叉树最大深度
思路:比较简单,求出左右子树的高度,取较大值。
public int maxDepth(TreeNode root) {
//空节点高度0
if(root==null)
return 0;
//求左子树高度
int hL=maxDepth(root.left);
//求右子树高度
int hR=maxDepth(root.right);
return Math.max(hL,hR)+1;
}
8 买卖股票的最佳时机
思路:动态规划,更新利益最大值和最低买入价。
public int maxProfit(int[] prices) {
if(prices.length==0)
return 0;
//保持最大利益
int max=0;
//保持最低买入价
int min=prices[0];
for(int i=1;i<prices.length;i++){
if(max<prices[i]-min)
max=prices[i]-min;
min=Math.min(min,prices[i]);
}
return max;
}
9 只出现一次的数字
思路:利用异或的特点,两个相同数字异或为0,异或整个数组剩下的数字就是结果。
public int singleNumber(int[] nums) {
int num=nums[0];
for(int i=1;i<nums.length;i++){
num^=nums[i];
}
return num;
}
10 环形链表
思路:利用快慢指针,如果相遇说明存在环,如果快指针遍历到null,说明不存在环。
public boolean hasCycle(ListNode head) {
ListNode slow=head;
ListNode fast=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast)
return true;
}
return false;
}
11 最小栈
思路:使用2个栈,s1存放所有操作数,s2只操作最小数。
class MinStack {
private Stack<Integer> s1=new Stack<>();
private Stack<Integer> s2=new Stack<>();
public MinStack() {
}
public void push(int x) {
s1.push(x);
//如果s2为空或者当前元素小于s2的栈顶元素
if(s2.isEmpty()||x<=s2.peek())
s2.push(x);
}
public void pop() {
//如果s1和s2栈顶元素一样,s2也要出栈一个
if(s1.peek().equals(s2.peek()))
s2.pop();
s1.pop();
}
public int top() {
return s1.peek();
}
public int getMin() {
return s2.peek();
}
}
12 相交链表
思路:双指针遍历,如果到头就从另一条链表遍历,相等时返回节点。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p=headA;
ListNode q=headB;
while(p!=q){
p=p!=null?p.next:headB;
q=q!=null?q.next:headA;
}
return p;
}
13 多数元素
思路:使用投票算法,如果当前数字和候选者值相同加1票,否则减1票,最终剩下的候选者就是最多的元素。
public int majorityElement(int[] nums) {
Integer leader=null;//初始时候选者不存在
int count=0;//票数自然也为0
for(int num:nums){
//如果票数为0,新的数字成为候选者
if(count==0)
leader=num;
//如果当前数字和候选者一样加1票,否则减1票
count+=leader==num?1:-1;
}
return leader;
}
14 打家劫舍
思路:动态规划,i房间可偷窃的最大值=(i-2房间可偷窃最大值+i房间的价值)和(i-1房间的最大值)中的较大值。
public int rob(int[] nums) {
if(nums.length==0)
return 0;
int[] dp=new int[nums.length+1];
//初始化
dp[0]=0;//防止i-2空指针异常,所以增加一个首元素
dp[1]=nums[0];
//动态规划
for(int i=2;i<dp.length;i++){
dp[i]=Math.max(dp[i-2]+nums[i-1],dp[i-1]);
}
return dp[dp.length-1];
}
15 反转链表
思路:使用三个指针,分别存储前一个节点,当前节点,下一个节点。
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode cur=head;
ListNode next=null;
while(cur!=null){
next=cur.next;
cur.next=pre;
pre=cur;
cur=next;
}
return pre;
}
16 反转二叉树
思路:翻转左右子树,反向赋值即可。
public TreeNode invertTree(TreeNode root) {
if(root!=null){
TreeNode left=invertTree(root.left);
TreeNode right=invertTree(root.right);
root.left=right;
root.right=left;
}
return root;
}
17 回文链表
思路:用ArrayList保持元素,比较前后是否一致。
public boolean isPalindrome(ListNode head) {
ArrayList<Integer> list=new ArrayList<>();
while(head!=null){
list.add(head.val);
head=head.next;
}
for(int i=0;i<list.size()/2;i++){
if(!list.get(i).equals(list.get(list.size()-1-i)))
return false;
}
return true;
}
18 移动零
思路:如果为0就将0计数器值加1,否则就按顺序填充一个位置,最后从末尾开始按0计数器个数填充0。
public void moveZeroes(int[] nums) {
//计算0元素的个数
int count=0;
//非0元素的存储下标
int index=0;
for(int i=0;i<nums.length;i++){
if(nums[i]==0)
count++;
else
nums[index++]=nums[i];
}
for(int i=nums.length-1;i>=nums.length-count;i--){
nums[i]=0;
}
}