160.给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。
提示:
listA 中节点数目为 m
listB 中节点数目为 n
1 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]
进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?
考虑哈希表,先存a,然后遍历b,如果哈希表中包含节点,返回该节点的值。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Map<Integer,ListNode> hashMap=new HashMap<Integer,ListNode>();
ListNode a=headA;
ListNode b=headB;
int i=0;
while(a!=null){
hashMap.put(i,a);
a=a.next;
i++;
}
//A链表全部存入hash表中
while(b!=null){
if(hashMap.containsValue(b)){
return b;
}
b=b.next;
}
return null;
}
}
双指针可以降低空间复杂度。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode a=headA;
ListNode b=headB;
if(a==null||b==null){
return null;
}
while(a!=b){
if(a==null&&b==null){
return null;
}
a=a==null?headB:a.next;
b=b==null?headA:b.next;
}
return a;
}
}
24.给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false
提示:
链表中节点数目在范围[1, 105] 内
0 <= Node.val <= 9
进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
class Solution {
public boolean isPalindrome(ListNode head) {
//获取链表长度
int num=0;
ListNode temp=head;
while(temp!=null){
temp=temp.next;
num++;
}
//此时temp指向链表末端,num为链表长度
//获取前半段链表并反转,双指针遍历两链表观察val是否一致。
ListNode t1=head;
Stack<ListNode> s=new Stack<ListNode>();
for(int i=0;i<num/2;i++){
s.push(t1);
t1=t1.next;
}
if(num%2!=0){
t1=t1.next;
}
//t1指向链表中间
while(t1!=null){
if(s.peek().val!=t1.val){
return false;
}
s.pop();
t1=t1.next;
}
return true;
}
}
使用栈来获取前半段链表的反转以后的值。
Other ways:1.复制到数组使用双指针判断是否回文-非常朴素的方法;2.递归;3.快慢指针
class Solution {
public boolean isPalindrome(ListNode head) {
//全部存入数组
ListNode temp=head;
List<Integer> list=new ArrayList<Integer>();
while(temp!=null){
list.add(temp.val);
temp=temp.next;
}
int front=0;
int tail=list.size()-1;
while(front<tail){
if(!list.get(front).equals(list.get(tail))){
return false;
}
front++;
tail--;
}
return true;
}
}
递归研究一下;笑死,根本没学会。
class Solution {
private ListNode frontPointer;
private boolean recursivelyCheck(ListNode currentNode) {
//只要当前指针没有遍历完链表,就继续走
if(currentNode!=null){
//从最后一个节点开始遍历,如果最后一个节点的值和前面节点的值不相同,直接false,
//直到最后一个节点走到了头结点,那么true
if(!recursivelyCheck(currentNode.next)){
return false;
}
if(currentNode.val!=frontPointer.val){
return false;
}
frontPointer=frontPointer.next;
}
return true;
}
public boolean isPalindrome(ListNode head) {
//全局变量指向头
frontPointer=head;
return recursivelyCheck(head);
}
}
20.给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
示例 1:
输入:s = "()"
输出:true
示例 2:输入:s = "()[]{}"
输出:true
示例 3:输入:s = "(]"
输出:false
示例 4:输入:s = "([)]"
输出:false
示例 5:输入:s = "{[]}"
输出:true
提示:
1 <= s.length <= 104
s 仅由括号 '()[]{}' 组成
class Solution {
public boolean isValid(String s) {
//思路:类似中缀表达式做计算器
//创建一个栈,遍历字符串,如果字符串为空,直接压栈,指向下一个;
//如果字符串不为空,看当前字符和栈顶字符是否匹配,如果不匹配,压栈,指向下一个;
//如果匹配,弹栈,指向下一个;最终如果栈为空返回true,否则返回false;
Stack<String> stack=new Stack<String>();
int index=0;
while(index<s.length()){
String c=s.substring(index,index+1);
if(stack.size()==0){
stack.push(c);
}else{
if(("(".equals(stack.peek()))&&(")".equals(c))){
stack.pop();
}else if(("[".equals(stack.peek()))&&("]".equals(c))){
stack.pop();
}else if(("{".equals(stack.peek()))&&("}".equals(c))){
stack.pop();
}else{
stack.push(c);
}
}
index++;
}
if(stack.size()==0){
return true;
}else{
return false;
}
}
}
注意:判断栈是否为空得用size为0,不能用null,不然会报空栈异常,是jdk版本问题;判断字符串的值是否相等得用equals,再用双等号我是sb。
70.假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
提示:
1 <= n <= 45
class Solution {
public int climbStairs(int n) {
//思路:第一反应是递归,爬到n层的方法有两种
//1.爬1层和爬到n-1层的方法次数;2.爬2层和爬到n-2层的方法次数
int res=0;
if(n==1){
return 1;
}
if(n==2){
return 2;
}
return climbStairs(n-1)+climbStairs(n-2);
}
}
对是对了,显示超时,明天研究一下其他方法!
1. 动态规划法
class Solution {
public int climbStairs(int n) {
int a=1;
int b=1;
int result=1;
for(int i=1;i<n;i++){
result=a+b;
a=b;
b=result;
}
return result;
}
}
2.斐波那契数列:注意,采用round做四舍五入后再强转,计算n要用n+1。
class Solution {
public int climbStairs(int n) {
//斐波那契数列
double a=Math.sqrt(5);
double b=Math.pow((1+a)/2,n+1)-Math.pow((1-a)/2,n+1);
return (int)Math.round(b/a);
}
}
3.矩阵快速幂,听都没听过。
散了吧,学会了也写不出来!
难度简单1341收藏分享切换为英文接收动态反馈
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于
⌊ n/2 ⌋
的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3] 输出:3示例 2:
输入:[2,2,1,1,1,2,2] 输出:2进阶:
- 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
第一反应是排序和哈希表。
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
}
哈希表的遍历有一点忘记了。
class Solution {
public int majorityElement(int[] nums) {
//用哈希表统计每个数的出现次数,然后寻找哈希表中最大的数对应的key
Map<Integer,Integer> hashMap=new HashMap<Integer,Integer>();
for(int i:nums){
if(hashMap.containsKey(i)){
//有这个数,让该key的value+1;
hashMap.put(i,hashMap.get(i)+1);
}else{
//没有这个数,把这个数作为key,给value一个1
hashMap.put(i,1);
}
}
//哈希表存完了,开始找哈希表里value最大的key
Iterator i=hashMap.entrySet().iterator();
int max=0;
int maxIndex=0;
//遍历哈希表-转set集合,用迭代器
Set<Map.Entry<Integer,Integer>> set=hashMap.entrySet();
for(Map.Entry<Integer,Integer> s:set){
if(s.getValue()>max){
max=s.getValue();
maxIndex=s.getKey();
}
}
return maxIndex;
}
}
1.随机化-什么阴间解法?画蛇添足了属于是
class Solution {
private int randRange(Random rand, int min, int max) {
return rand.nextInt(max - min) + min;
}
private int countOccurences(int[] nums, int num) {
int count = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == num) {
count++;
}
}
return count;
}
public int majorityElement(int[] nums) {
Random rand = new Random();
int majorityCount = nums.length / 2;
while (true) {
int candidate = nums[randRange(rand, 0, nums.length)];
if (countOccurences(nums, candidate) > majorityCount) {
return candidate;
}
}
}
}
分治和投票算法还没学过。
2.投票算法-如果不是众数当选,众数投反对票,一定会发生换届;直到众数当选,最终众数获票一定大于0.
class Solution {
public int majorityElement(int[] nums) {
//投票算法搞一哈
int number=0;
int count=0;
for(int i:nums){
if(count==0){
number=i;
}
if(i==number){
count++;
}else{
count--;
}
}
return number;
}
}
3.分治算法
学完再来做。。
283.给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:输入: nums = [0]
输出: [0]
提示:
1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
进阶:你能尽量减少完成的操作次数吗?
第一想法类似冒泡排序。
class Solution {
public void moveZeroes(int[] nums) {
//暴力解法-类似冒泡排序,把所有为0的泡泡和右边的泡泡交换
for(int i=0;i<nums.length-1;i++){
for(int j=i+1;j<nums.length;j++){
if(nums[i]==0){
//交换
nums[i]=nums[j];
nums[j]=0;
}
}
}
}
}
1.双指针-左指针指向头不动,右指针一旦遇到不为0的数就丢给左指针,左指针移动,当右指针走到末尾时,将左指针与末尾之间的所有数置0.
class Solution {
public void moveZeroes(int[] nums) {
//双指针搞一哈
int left=0;
for(int i=0;i<nums.length;i++){
if(nums[i]!=0){
nums[left]=nums[i];
left++;
}
}
for(int i=left;i<nums.length;i++){
nums[i]=0;
}
}
}
155.设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
示例 1:
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]输出:
[null,null,null,null,-3,null,0,-2]解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
提示:
-231 <= val <= 231 - 1
pop、top 和 getMin 操作总是在 非空栈 上调用
push, pop, top, and getMin最多被调用 3 * 104 次
1.考虑用空间换时间
class MinStack {
Stack<Integer> stack;
Stack<Integer> minStack;
public MinStack() {
stack=new Stack<Integer>();
minStack=new Stack<Integer>();
}
public void push(int val) {
stack.push(val);
if(minStack.size()!=0){
minStack.push(Math.min(val,minStack.peek()));
}else{
minStack.push(val);
}
}
public void pop() {
stack.pop();
minStack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
//minStack.push(Integer.MAX_VALUE);最大的Integer
注意:初始化要写成成员变量;判断栈空要用size为0;警惕空栈调用peek。
2.不允许使用额外空间的情况
-底层不写栈,写链表。注意点在于next要指向前一个节点。
class MinStack {
public Node head;
public void push(int x) {
if(head==null){
head=new Node(x,x);
}else{
head=new Node(x,Math.min(x,head.min),head);
}
}
public void pop() {
head=head.next;
}
public int top() {
return head.val;
}
public int getMin() {
return head.min;
}
}
class Node{
public Node next;
public int val;
public int min;
Node(){
}
Node(int val,int min){
this.val=val;
this.min=min;
this.next=null;
}
Node(int val,int min,Node temp){
this.val=val;
this.min=min;
this.next=temp;
}
}
-保存差值,以后再研究。
leetcode 155. 最小栈 ——O(1)额外空间_jokerMingge的博客-CSDN博客
11.给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:输入:height = [1,1]
输出:1
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
1.双指针,每次让小的那个数的指针移动。
class Solution {
public int maxArea(int[] height) {
int square=0;
int left=0;
int right=height.length-1;
while(left<right){
int temp=(right-left)*(Math.min(height[right],height[left]));
if(square<temp){
square=temp;
}
if(height[left]>height[right]){
right--;
}else{
left++;
}
}
return square;
}
}
121.给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104
感觉和上面那个题有异曲同工之妙!
class Solution {
public int maxProfit(int[] prices) {
int maxMoney=0;
int min=Integer.MAX_VALUE;
int j;
for(int i=0;i<prices.length;i++){
if(prices[i]<min){
min=prices[i];
}else if(prices[i]-min>maxMoney){
//记录了前面的最大利润,哪怕后面min发生改变,只要没有超过前面的最大利润,不影响解答
//譬如 2 8 1 3,虽然后面min里记录的是1,可是最大利润一直记录的是8-2=6;
maxMoney=prices[i]-min;
}
}
return maxMoney;
}
}
maxMoney记录了前面的最大利润,哪怕后面min发生改变,只要没有超过前面的最大利润,不影响解答。譬如 2 8 1 3,虽然后面min里记录的是1,可是最大利润一直记录的是8-2=6。(!!!)
64.给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
示例 2:输入:grid = [[1,2,3],[4,5,6]]
输出:12
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 200
0 <= grid[i][j] <= 100
感觉类似迷宫问题,第一反应是递归法,暴力的话应该是遍历所有路径找到最小的?结果官方给的是动态规划。
class Solution {
public int minPathSum(int[][] grid) {
//格子有三种,上面一排、左边一列和其他。
//上面一排只能从他的左边移动过来
//左边一列只能从上面移动过来
//其他就要判断最小路径了
int[][] dp=grid;//dp里面存走到每个点的最小路径
dp[0][0]=grid[0][0];
for(int i=0;i<grid.length;i++){
for(int j=0;j<grid[i].length;j++){
if(j==0&&i>0){
dp[i][0]=dp[i-1][0]+grid[i][0];
}
if(i==0&&j>0){
dp[0][j]=dp[0][j-1]+grid[0][j];
}
if(i>0&&j>0){
dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
}
int x=grid.length-1;
int y=grid[0].length-1;
return grid[x][y];
}
}
没有考虑输入为null的情况!!!!而且可以先写好第一行和第一列,可以降低复杂度。
class Solution {
public int minPathSum(int[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0) {
return 0;
}
int rows = grid.length, columns = grid[0].length;
int[][] dp = new int[rows][columns];
dp[0][0] = grid[0][0];
for (int i = 1; i < rows; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int j = 1; j < columns; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
for (int i = 1; i < rows; i++) {
for (int j = 1; j < columns; j++) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[rows - 1][columns - 1];
}
}