1. 21【合并两个有序链表】- 双指针
- 题目: 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。(链表)
- 代码:
java
复制代码
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode node = new ListNode();
ListNode ans = node;
while(list2!=null && list1!=null){
if(list1.val<=list2.val){
node.next = list1;
list1 = list1.next;
}else{
node.next = list2;
list2 = list2.next;
}
node = node.next;
}
if(list1==null){
node.next = list2;
}else{
node.next = list1;
}
return ans.next;
}
}
2. 136【只出现一次的数字】- 位运算
- 题目: 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
- 代码:
java
复制代码
class Solution {
public int singleNumber(int[] nums) {
//利用异或运算,相同为0,不同为1
//0和任何数进行异或运算=数本身
//任何数自身异或=0
int ans = 0;
for(int i=0;i<nums.length;i++){
ans ^= nums[i];
}
return ans;
}
}
3. 155【最小栈】- 原始栈+最小栈
- 题目: 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
- MinStack() 初始化堆栈对象。
- void push(int val) 将元素val推入堆栈。
- void pop() 删除堆栈顶部的元素。
- int top() 获取堆栈顶部的元素。
- int getMin() 获取堆栈中的最小元素。
- 代码:
java
复制代码
class MinStack {
//同时维护一个最小元素栈,当调用最小元素方法时,将最小元素栈顶弹出
//最小元素栈构建方法
//每次向栈内加入元素,就要判断该元素是否比最小元素栈栈顶小,
//如果小,则加入该元素,否则加入栈顶元素
//每次弹出元素时,也要弹出最小元素栈栈顶
Deque<Integer> stack;
Deque<Integer> minStack;
public MinStack() {
stack = new LinkedList<>();
minStack = new LinkedList<>();
}
public void push(int val) {
if(minStack.isEmpty()){
minStack.push(val);
}else if(val < minStack.peek()){
minStack.push(val);
}else{
minStack.push(minStack.peek());
}
stack.push(val);
}
public void pop() {
stack.pop();
minStack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
4. 169【多数元素】- 排序取中间值/HashMap
- 题目: 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
- 代码:
java
复制代码
//法一:
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
int i = nums.length/2;
return nums[i];
}
}
//法二:
class Solution {
public int majorityElement(int[] nums) {
//用map的key表示元素,value表示出现的次数
HashMap<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
int count = map.getOrDefault(nums[i],0)+1;
if(count > nums.length/2){
return nums[i];
}
map.put(nums[i],count);
}
return 0;
}
}
5. 146【LRU 缓存】- HashMap+双向链表
- 题目: 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
- 代码:
java
复制代码
class Node{
int value;
int key;
Node next;
Node pre;
public Node(){
}
public Node(int value){
this.value = value;
}
}
class LRUCache {
//使用一个具有虚拟头节点和虚拟伪节点的双向链表实现缓存表
//利用HashMap实现O(1)add和delete
//key是缓存表里的值,value是缓存表中的节点
//每次put,get都要将其对应的节点放在头节点的下一个
//当缓存表满之后,删除尾节点的上一个
public int count;
public int capacity;
public HashMap<Integer,Node> map;
public Node head,tail;
public LRUCache(int capacity) {
this.count = 0;
this.capacity = capacity;
this.map = new HashMap<Integer,Node>();
this.head = new Node();
this.tail = new Node();
head.next = tail;
head.pre = null;
tail.next = null;
tail.pre = head;
}
public int get(int key) {
if(map.containsKey(key)){
int value = map.get(key).value;
delete(key);
addToHead(key,value);
return value;
}else{
return -1;
}
}
public void put(int key, int value) {
if(map.containsKey(key)){
delete(key);
}else{
if(count >= capacity){
Node tmpNode = tail.pre;
Node preNode = tmpNode.pre;
preNode.next = tail;
tail.pre = preNode;
map.remove(tmpNode.key);
}else{
count++;
}
}
addToHead(key,value);
}
public void addToHead(int key,int value){
Node tmpNode = new Node(value);
tmpNode.key = key;
tmpNode.next = head.next;
tmpNode.pre = head;
head.next.pre = tmpNode;
head.next = tmpNode;
map.put(key,tmpNode);
}
public void delete(int key){
Node tmpNode = map.get(key);
Node preNode = tmpNode.pre;
Node nextNode = tmpNode.next;
preNode.next = nextNode;
nextNode.pre = preNode;
}
}
6. 31【下一个排列】- 数组反转
- 题目: 整数数组的一个排列就是将其所有成员以序列或线性顺序排列。
- 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
- 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
- 而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。必须 原地 修改,只允许使用额外常数空间。
- 代码:
java
复制代码
class Solution {
public void nextPermutation(int[] nums) {
//由后向前找到第一个nums[i]>nums[i-1]的位置,
//然后由i向后找,找到最后一个大于nums[i-1]的数,将该数与i-1处的数交换位置
//最后将[i,length-1]区间的数反转,得到结果
//如果题中给出的是最后一个排列,则直接将所有数进行反转
int tmp,index;
for(int i=nums.length-1;i>0;i--){
if(nums[i]>nums[i-1]){
index = i;
tmp = nums[i-1];
while(index<nums.length && tmp < nums[index]){
index++;
}
if(index != i){
nums[i-1] = nums[--index];
}else{
nums[i-1] = nums[i];
}
nums[index] = tmp;
swap(nums,i,nums.length-1);
return;
}
}
swap(nums,0,nums.length-1);
}
public void swap(int[] nums,int left,int right){
while(left<right){
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
left++;
right--;
}
}
}
7. 347【前K个高频元素】- 大根堆
- 题目: 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
- 代码:
java
复制代码
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//首先,统计每个元素出现的次数,使用HashMap
//然后对出现次数进行排序
//因为题中要求出现频率前k高,所以使用大根堆
HashMap<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
}
PriorityQueue<int[]> heap = new PriorityQueue<>(new Comparator<int[]>(){
public int compare(int[] a, int[] b){
return b[1]-a[1];
}
});
for(Map.Entry<Integer,Integer> entry : map.entrySet()){
heap.offer(new int[]{entry.getKey(),entry.getValue()});
}
int[] ans = new int[k];
for(int i=0;i<k;i++){
ans[i] = heap.poll()[0];
}
return ans;
}
}
8. 560【和为 K 的子数组】- 双指针/HashMap
- 题目: 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。子数组是数组中元素的连续非空序列。
- 代码:
java
复制代码
//方法一
class Solution {
public int subarraySum(int[] nums, int k) {
//双指针,左指针固定,右指针不断向右滑动
//因为nums中有负值,所以需要一直遍历到最后才能终止
int n = 0;
for(int i=0;i<nums.length;i++){
int sum = nums[i];
if(sum == k){
n++;
}
for(int j=i+1;j<nums.length;j++){
sum += nums[j];
if(sum == k){
n++;
}
}
}
return n;
}
}
//方法二
class Solution {
public int subarraySum(int[] nums, int k) {
//连续非空序列,也就是说这个序列区间[i,j]内元素的和可以由
//sum[0,j]-sum[0,i]得出
//将前i个元素的和存储在hashmap中,
//如果存在sum[i]-k,那么则证明有子序列的和等于k
//用value存放sum[i]出现的次数
HashMap<Integer,Integer> map = new HashMap<>();
int n = 0;
int sum = 0;
map.put(0,1);
for(int i=0;i<nums.length;i++){
sum += nums[i];
if(map.containsKey(sum-k)){
n += map.get(sum-k);
}
map.put(sum,map.getOrDefault(sum,0)+1);
}
return n;
}
}
9. 118【杨辉三角】 - 动态规划
- 题目: 给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。在「杨辉三角」中,每个数是它左上方和右上方的数的和。
- 代码:
java
复制代码
class Solution {
public List<List<Integer>> generate(int numRows) {
//dp表示第i行j列的值
//dp[i][j] = dp[i-1][j-1]+dp[i-1][j]
//dp[0][]=1,dp[i][i]=0
List<List<Integer>> ans = new ArrayList<>();
for(int i=0;i<numRows;i++){
List<Integer> row = new ArrayList<>();
for(int j=0;j<=i;j++){
if(j==0 || i==j){
row.add(1);
}
else{
int a = ans.get(i-1).get(j-1);
int b = ans.get(i-1).get(j);
row.add(a+b);
}
}
ans.add(row);
}
return ans;
}
}
10. 198【打家劫舍】- 动态规划
- 题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
- 代码:
java
复制代码
class Solution {
public int rob(int[] nums) {
//dp表示在前i个屋子能偷窃到的最高金额
//dp[i] = max(dp[i-1],dp[i-2]+v[i])
//dp[0] = v[0],dp[1] = max(v[0],v[1])
if(nums.length == 1){
return nums[0];
}
int[] dp = new int[nums.length];
dp[0] = nums[0];
dp[1] = Math.max(nums[0],nums[1]);
for(int i=2;i<nums.length;i++){
dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.length-1];
}
}
11. 279【完全平方数】- 动态规划
- 题目: 给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
- 代码:
java
复制代码
class Solution {
public int numSquares(int n) {
//先找到所有<=n的完全平方数w
//n就相当于最大重量,w是每个石头的重量,最少数量是价值
//dp[i][j]=min(dp[i-1][j],dp[i][j-w]+1)
//dp[0][i]=i,dp[][0]=0
List<Integer> w = new ArrayList<>();
for(int i=1;i<=n/2;i++){
if(i*i > n) break;
if(i*i == n) return 1;
w.add(i*i);
}
int[] dp = new int[n+1];
for(int i=0;i<=n;i++) {
dp[i] = i;
}
for(int i=1;i<w.size();i++){
for(int j=1;j<=n;j++){
if(j<w.get(i)){
dp[j] = dp[j];
}else{
dp[j] = Math.min(dp[j],dp[j-w.get(i)]+1);
}
}
}
return dp[n];
}
}
12. 3【无重复字符的最长子串】- 双指针+HashMap
- 题目: 给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。
- 代码:
java
复制代码
class Solution {
public int lengthOfLongestSubstring(String s) {
//根据题意,s的长度可能为0,因此判断s.length是否等于零
//双指针,左指针固定,移动右指针,每移动一位都要加入HashMap中
//如果有重复元素,判断这个重复元素是否在当前窗口
//如果在,将左指针指向这个元素的下一位,否则不更改左指针的值
//更新HashMap中key为重复元素的value值
if(s.length() == 0) return 0;
int max = 0;
int j = 0;
HashMap<Character,Integer> map = new HashMap<>();
for(int i=0;i<s.length();i++){
if(map.containsKey(s.charAt(i))){
j = Math.max(j,map.get(s.charAt(i))+1);
}
max = max>i-j+1? max: i-j+1;
map.put(s.charAt(i),i);
}
return max;
}
}
13. 739【每日温度】- 单调栈
- 题目: 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
- 代码:
java
复制代码
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
//利用单调栈,因为题中要求找更高的温度,所以栈是单调递减的
//为了便于寻找位置,栈中维护每个元素对应的下标
//由左向右遍历数组,如果栈为空,则将该元素加入栈
//如果栈中有元素,且比当前元素小,则将栈中元素的answer设置为当前元素的下标-栈顶元素
//然后将栈顶元素出栈,重复比较当前元素与栈顶元素的值,直到栈为空,或没有比栈顶元素更小的元素
//如果栈中有元素,且比当前元素大,则将当前元素对应下标入栈
int[] answer = new int[temperatures.length];
Deque<Integer> stack = new LinkedList<>();
for(int i=0;i<temperatures.length;i++){
while(!stack.isEmpty() && temperatures[stack.peek()]<temperatures[i]){
int j = stack.pop();
answer[j] = i-j;
}
stack.push(i);
}
return answer;
}
}