7.整数反转
32位有符号整数的范围是[-2147483648,2147483647],要判断是否溢:以正数范围为例,除掉最后一位,①前面n-1位要是大于214748364,那么这个数肯定是溢出了;②前面n-1位刚好等于214748364,那就要判断个位是否大于7,如果大于7就溢出了。
class Solution {
public int reverse(int x) {
int res=0;
while(x!=0){
int temp=x%10;
if(res>214748364||(res==214748364&&temp>7)){
return 0;
}
if(res<-214748364||(res==-214748364&&temp<-8)){
return 0;
}
res=res*10+temp;//每次将res更新为上一次循环的取余结果乘10再加上这一次循环的取余结果
x=x/10;//每一次都把x的最后一位去掉
}
return res;
}
}
9.回文数
首先小于0肯定不是回文数,因为会有负号。再考虑大于0的情况,分别对比第一位和最后一位,相同的话将这两位去掉再循环对比。
class Solution {
public boolean isPalindrome(int x) {
if(x<0){
return false;
}
int maxbit=1;
//获取x的位数
//判定条件是当x除以初始位1(默认只有个位)大于等于10的时候,证明位数大一位
while(x/maxbit>=10){
maxbit*=10;
}
while(x>0){
int left=x/maxbit;//最高位
int right=x%10;//最低位
if(left!=right){
return false;
}
x=(x%maxbit)/10;
maxbit/=100;//因为去掉了最高位和最低位,共两位
}
return true;
}
}
13.罗马数字转整数
I | V | X | L | C | D | M |
---|---|---|---|---|---|---|
1 | 5 | 10 | 50 | 100 | 500 | 1000 |
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
- I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
- X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
- C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
可以由此看出规律:当小的放在大的前面时,就不是加法叠加了,变成了减法:
- IV→V-I=V+(-I)=5-1=4
- IX→X-I=X+(-I)=10-1=9
- XL→L-X=L+(-X)=50-10=40
- XC→C-X=C+(-X)=100-10=90
- CD→D-C=D+(-C)=500-100=400
- CM→M-C=M+(-C)=1000-100=900
小的放大的前面的时候,把小的表示的数变成负数,再从左到右叠加。
class Solution {
public int romanToInt(String s) {
int sum=0;
//charAt()方法用于返回指定索引处的字符。索引范围为从0到length()-1。
int firstNum=getValue(s.charAt(0));
for(int i=1;i<s.length();i++){
int num=getValue(s.charAt(i));
if(firstNum<num){//当前一位小于后一位时,将小的那位变成负数
sum-=firstNum;
}
else{
sum+=firstNum;
}
firstNum=num;//后移一位做第一位
}
sum+=firstNum;
return sum;
}
private int getValue(char ch){
switch(ch){
case 'I':return 1;
case 'V':return 5;
case 'X':return 10;
case 'L':return 50;
case 'C':return 100;
case 'D':return 500;
case 'M':return 1000;
default:return 0;
}
}
}
14.最长公共前缀
先循环字符串数组,再循环字符串。设定数组的第一个字符串为默认公共前缀开始循环,然后和后面的字符串开始比较,不相同就把默认公共前缀去掉最后一位,直到长度为0。
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs.length==0) return "";
String common=strs[0];
for(int i=1;i<strs.length;i++){
//indexOf:返回指定字符在字符串中第一次出现处的索引,没有则返回-1
while(strs[i].indexOf(common)!=0){
common=common.substring(0,common.length()-1);//减小判定字符的长度
if(common.length()==0) return "";
}
}
return common;
}
}
21.合并两个有序链表
递归的思路是,判断两个列表里的第一个结点的数值哪个更小,然后把小的next指针指向两个链表中其余数的合并结果。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}
else if(l2==null){
return l1;
}
else if(l1.val<=l2.val){
l1.next=mergeTwoLists(l1.next,l2);
return l1;
}
else{
l2.next=mergeTwoLists(l2.next,l1);
return l2;
}
}
}
26.删除排序数组中的重复项
用双指针,具体写在注释里。
class Solution {
public int removeDuplicates(int[] nums) {
//有序数组说明重复的元素都是挨着的
if(nums.length==0) return 0;
int i=0;//前指针
int j=1;//后指针
while(j<nums.length){//循环到数组尾停止
if(nums[i]==nums[j]){//后和前相等时,移动后指针,跳过重复元素
j++;
}
else{//后和前不相等时(说明i和j不挨着,(i+1)!=j),前指针指向的元素的下一位置换成j所在元素的数值
nums[i+1]=nums[j];
i++;
}
}
return i+1;
}
}
27.移除元素
双指针,跟上面差不多。i就是用来从第0位开始放置不与val相等的元素,j就是用来判断当前元素是否等于val。
class Solution {
public int removeElement(int[] nums, int val) {
int i=0;
for(int j=0;j<nums.length;j++){
if(nums[j]!=val){
nums[i]=nums[j];
i++;
}
}
return i;
}
}
34.在排序数组中查找元素的第一个和最后一个位置
二分查找。找边界跟传统的二分查找模板差了个target与mid值相等的时候,因为mid值等于target不代表mid就是边界。而且还要对数组中没有target进行判断。
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums.length==0){
return new int[]{-1,-1};
}
int[] ret=new int[]{searchStart(nums,target),searchEnd(nums,target)};
return ret;
}
private int searchStart(int[] nums,int target){
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]<target){
left=mid+1;
}
else if(nums[mid]>target||nums[mid]==target){
right=mid-1;
}
}
if(left>=nums.length||nums[left]!=target)
{
return -1;
}
return left;
}
private int searchEnd(int[] nums,int target){
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]<target||nums[mid]==target){
left=mid+1;
}
else if(nums[mid]>target){
right=mid-1;
}
}
if(right<0||nums[right]!=target)
{
return -1;
}
return right;
}
}
35.搜索插入位置
上午看了很久的二分查找的解题思路,总算是完完全全看懂了,多尝试一下希望可以举一反三。
此处贴上大佬的套路框架,非常详细:https://labuladong.gitbook.io/algo/di-ling-zhang-bi-du-xi-lie/er-fen-cha-zhao-xiang-jie
这道题是二分查找的微微变种,在排序数组里找到了,就是正常的二分查找,没找到,要返回插入的位置。
假设我们给的数组是[...,3,5,...]。现在要找的数字是4。到最后定位到left==right的时候,要么在3的位置,要么在5的位置。假如最后定位在3的位置,nums[mid] < target,然后left = mid + 1,最后返回的left就是3后面那个位置,符合要求。假如最后定位在5的位置,nums[mid] > target, 此时更新的是right,与left无关,最后返回的left就是5所在的位置,符合要求。
class Solution {
public int searchInsert(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while(left<=right){
int mid = left+(right-left)/2;
if(nums[mid]==target){
return mid;
}
else if(nums[mid]<target){
left=mid+1;
}
else if(nums[mid]>target){
right=mid-1;
}
}
return left;
}
}
58.最后一个单词的长度
lastIndexOf():返回指定子字符串在此字符串中最右边出现处的索引。
class Solution {
public int lengthOfLastWord(String s) {
if(s==null||s.length()==0) return 0;
s=s.trim();
return s.length()-1-s.lastIndexOf(" ");
}
}
66.加一
class Solution {
public int[] plusOne(int[] digits) {
for(int i=digits.length-1;i>=0;i--){
if(digits[i]!=9){
//最后一位不为9就直接return了,不会循环到前面位
digits[i]+=1;
return digits;
}
//最后一位为9时不会return,还会再进行一次循环,进位,让前一位+1之后再return
digits[i]=0;
}
//当所有位都是9的时候,数组长一位,并且第一位为1,后面全是0
int []digits9=new int[digits.length+1];
digits9[0]=1;
return digits9;
}
}
69.x的平方根
二分法。在0到x内找一个数使得平方为target。
class Solution {
public int mySqrt(int x) {
int ans=-1;
int left=0;
int right=x;
while(left<=right){
int mid=left+(right-left)/2;
if ((long)mid*mid<=x) {
ans=mid;
left=mid+1;
}
else{
right=mid-1;
}
}
return ans;
}
}
83.删除排序列表中的重复元素
有序链表,说明重复的元素是挨着的。
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode i=head;
while(i!=null&&i.next!=null){
if(i.next.val==i.val){
i.next=i.next.next;
}
else{
i=i.next;
}
}
return head;
}
}
100.相同的树
递归,分别比较左子树和右子树。
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p==null&&q==null) return true;
if(p==null||q==null) return false;
if(p.val!=q.val) return false;
return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
}
}
101.对称二叉树
递归递归,分别判断左子树的左孩子是否和右子树的右孩子相同、左子树的右孩子是否和右子树的左孩子相同。
class Solution {
public boolean isSymmetric(TreeNode root) {
return isMirror(root,root);
}
public boolean isMirror(TreeNode r1,TreeNode r2){
if(r1==null&&r2==null) return true;
if(r1==null||r2==null) return false;
if(r1.val==r2.val&&isMirror(r1.left,r2.right)&&isMirror(r1.right,r2.left)) return true;
return false;
}
}
104.二叉树的最大深度
递归递归递归,深度=1+左右子树中深度最大的。
import java.lang.Math;
class Solution {
public int maxDepth(TreeNode root) {
if(root==null) return 0;
int lchild=maxDepth(root.left);
int rchild=maxDepth(root.right);
return 1+Math.max(lchild,rchild);
}
}
111.二叉树的最小深度
递归,这个有需要注意的地方,最小深度是从根节点到最近叶子节点的最短路径上的节点数量,而叶子节点是没有子节点的节点。所以当左右子树有一个为空另一个不为空时,根节点不是叶子节点,所以要继续向不为空的子树找叶子节点。当左右子树都为空的时候,根节点就是叶子节点,所以返回最小深度是1。第一次提交时没有考虑这种情况,就在[1,2]上报错了:
import java.lang.Math;
class Solution {
public int minDepth(TreeNode root) {
if(root==null) return 0;
if(root.left==null&&root.right==null) return 1;
if(root.left==null&&root.right!=null) return 1+minDepth(root.right);
if(root.right==null&&root.left!=null) return 1+minDepth(root.left);
int lchild=minDepth(root.left);
int rchild=minDepth(root.right);
return 1+Math.min(lchild,rchild);
}
}
112.路径总和
还是递归。先判断根节点是否为空;再判断左右子树为空的时候很节点是否跟已给数值相同;最后判断都不为空的情况,每路过一次就从已给数值中减去这个数,最后看减完是否为0。
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if(root==null) return false;
if(root.val==sum&&root.left==null&&root.right==null) return true;
return hasPathSum(root.left,sum-root.val)||hasPathSum(root.right,sum-root.val);
}
}
167.两数之和Ⅱ-输入有序数组
第一种方法:双指针,一前一后,因为数组是有序的,所以当左右指针指向的数值和小于目标数值时,左指针右移。大于目标数值时,右指针左移。没有就返回null。
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left=0;
int right=numbers.length-1;
while(left<right){
int sum=numbers[left]+numbers[right];
if(sum==target){
return new int[]{left+1,right+1};
}
else if(sum<target){
left++;
}
else{
right--;
}
}
return null;
}
}
第二种方法:二分查找。先对数组从头到尾循环,再对剩余的数组进行二分查找。通过了,但是贼慢。
class Solution {
public int[] twoSum(int[] numbers, int target) {
for(int i=0;i<numbers.length;i++){
int left=i+1;
int right=numbers.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(numbers[mid]==(target-numbers[i])){
return new int[]{i+1,mid+1};
}
else if(numbers[mid]<(target-numbers[i])){
left=mid+1;
}
else{
right=mid-1;
}
}
}
return null;
}
}
226.翻转二叉树
又是递归。就是先交换左右子树,再交换左右节点。
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) return null;
TreeNode ltree=invertTree(root.right);
TreeNode rtree=invertTree(root.left);
root.left=ltree;
root.right=rtree;
return root;
}
}
278.第一个错误的版本
二分法。
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left=1;
int right=n;
int wrong=-1;
while(left<=right){
int mid=left+(right-left)/2;
if(isBadVersion(mid)){
wrong=mid;
right=mid-1;
}
else{
left=mid+1;
}
}
return wrong;
}
}
367.有效的完全平方数
二分法二分法二分法,注意mid*mid要转换成double类型,不然结果会溢出。
class Solution {
public boolean isPerfectSquare(int num) {
int left=1;
int right=num;
while(left<=right){
int mid=left+(right-left)/2;
if((double)mid*mid==num){
return true;
}
else if((double)mid*mid<num){
left=mid+1;
}
else{
right=mid-1;
}
}
return false;
}
}
374.猜数字大小
二分法,很标准
class Solution {
public boolean isPerfectSquare(int num) {
int left=1;
int right=num;
while(left<=right){
int mid=left+(right-left)/2;
if((double)mid*mid==num){
return true;
}
else if((double)mid*mid<num){
left=mid+1;
}
else{
right=mid-1;
}
}
return false;
}
}
404.左叶子之和
只有根节点时,根节点不算叶子节点,要返回0。之前一直返回root值结果不对。
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
//根节点为空或者只有根节点时
if(root==null||(root!=null&&root.left==null&&root.right==null)) return 0;
//左子树为空,返回右子树的左叶子
if(root.left==null) return sumOfLeftLeaves(root.right);
//左子树不为空且没有孩子,说明是左叶子节点,返回左值+右树的左叶子
if(root.left!=null&&root.left.left==null&&root.left.right==null) return root.left.val+sumOfLeftLeaves(root.right);
return sumOfLeftLeaves(root.left)+sumOfLeftLeaves(root.right);
}
}
疑问 如下错解
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
//根节点为空
if(root==null) return 0;
//只有根节点
if(root!=null&&root.left==null&&root.right==null) return root.val;
//左子树为空,返回右子树的左叶子
if(root.left==null) return sumOfLeftLeaves(root.right);
//左子树不为空且没有孩子,说明是左叶子节点,返回左值+右树的左叶子
if(root.left!=null&&root.left.left==null&&root.left.right==null) return root.left.val+sumOfLeftLeaves(root.right);
return sumOfLeftLeaves(root.left)+sumOfLeftLeaves(root.right);
}
}
为什么对根节点是否是左叶子节点的判断错误会导致测试用例的结果不对呢?待思考。
441.排列硬币
等差数列,用等差数列求和公式解答,取证k,需要注意的是2*n会导致越界。2写成2.0会把结果变成double类型,就不会越界了。
class Solution {
public int arrangeCoins(int n) {
return (int)(Math.sqrt(2.0*n+0.25)-0.5);
}
}
704.二分查找
好蠢的题,直接考二分查找的框架代码。
class Solution {
public int search(int[] nums, int target) {
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]==target){
return mid;
}
else if(nums[mid]<target){
left=mid+1;
}
else if(nums[mid]>target){
right=mid-1;
}
}
return -1;
}
}
1010.总持续时间可被60整除的歌曲
暴力解法不可取:
class Solution {
public int numPairsDivisibleBy60(int[] time) {
int count=0;
for(int i=0;i<time.length;i++){
for(int j=i+1;j<time.length;j++) {
if((time[i]+time[j])%60==0){
count++;
}
}
}
return count;
}
}
1351.统计有序矩阵中的负数
在二分查找标签里,不过感觉循环也还可以啊。二分查找就找第一个为负的数时用(因为是有序的)。
class Solution {
public int countNegatives(int[][] grid) {
int count=0;
for(int i=0;i<grid.length;i++){
int []nums=grid[i];
for(int j=0;j<nums.length;j++){
if(nums[j]<0){
count+=(nums.length-j);
break;
}
}
}
return count;
}
}