my-leetcode

for循环热身:

目录:

1.计算0到100之间的oddSum 和 evenSum:

2.循环输出1-1000之间能被5整除的数,并且每行输出3个:

3.打印九九乘法表(j代表行数,i代表列数):

4.119. 杨辉三角 II

5.1. 两数之和

6.7. 整数反转

7.258. 各位相加

8.206. 反转链表

1.计算0到100之间的oddSum 和 evenSum:

    public static void main(String args[]){
        int oddSum = 0;
        int evenSum = 0;
        for(int i=0; i <=100; i++) {
            if(i%2 != 0) {
                oddSum+=i;
            } else {
                evenSum+=i;
            }
        }
        System.out.println("oddSum: "+oddSum);
        System.out.println("evenSum: "+evenSum);
    }

2.循环输出1-1000之间能被5整除的数,并且每行输出3个:

    public static void main(String args[]){
        for(int i=0; i <= 1000; i++) {
            if(i%5==0) {
                System.out.print(i+"	");
            }
            if(i%(5*3)==0) {
                System.out.println();
            }
        }
    }

3.打印九九乘法表(j代表行数,i代表列数):

    public static void main(String args[]){
        for(int j=1; j <= 9; j++) {
            for(int i=1; i<=j; i++) {
                System.out.print(i+"*"+j+"="+(j*i)+"	");
            }
            System.out.println();
        }
    }

leetcode正题开始:


119. 杨辉三角 II

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 行。(在杨辉三角中,每个数是它左上方和右上方的数的和)

说明:杨辉三角,是二项式系数在三角形中的一种几何排列。

方法一:依据 每个数字等于上一行的左右两个数字之和,即第 nn 行的第 ii 个数等于第 n-1n1 行的第 i-1i1 个数和第 ii 个数之和。

 

 1. List C保存了杨辉三角的全部(rowIndex)行的 所有数据;List row代表了其中的某一行的数据:

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<List<Integer>> C = new ArrayList<List<Integer>>();
        for (int i = 0; i <= rowIndex; ++i) {
            List<Integer> row = new ArrayList<Integer>();
            for (int j = 0; j <= i; ++j) {
                if (j == 0 || j == i) {
                    row.add(1);
                } else {
                    row.add(C.get(i - 1).get(j - 1) + C.get(i - 1).get(j));
                }
            }
            C.add(row);
        }
        return C.get(rowIndex);
    }
}

2. 优化:注意到对第 i+1 行的计算仅用到了第 行的数据,因此可以使用滚动数组的思想优化空间复杂度。

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> pre = new ArrayList<Integer>();
        for (int i = 0; i <= rowIndex; ++i) {
            List<Integer> cur = new ArrayList<Integer>();
            for (int j = 0; j <= i; ++j) {
                if (j == 0 || j == i) {
                    cur.add(1);
                } else {
                    cur.add(pre.get(j - 1) + pre.get(j));
                }
            }
            pre = cur;
        }
        return pre;
    }
}

3. 进一步优化:能否只用一个数组呢?

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> row = new ArrayList<Integer>();
        row.add(1);
        for (int i = 1; i <= rowIndex; ++i) {
            row.add(0);
            for (int j = i; j > 0; --j) {
                row.set(j, row.get(j) + row.get(j - 1));
            }
        }
        return row;
    }
}

 

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> row = new ArrayList<Integer>();
        row.add(1);
        for (int i = 1; i <= rowIndex; ++i) {
            row.add((int) ((long) row.get(i - 1) * (rowIndex - i + 1) / i));
        }
        return row;
    }
}


1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。

方法一:暴力枚举(把所有可能情况都考虑一遍)

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int n = nums.length;
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[0];
    }
}

方法二:哈希表(HashMap实现,将依次判断为空的元素,存入hashMap,后续的判断和已传入Map中的元素进行匹配)

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; ++i) {
            if (hashtable.containsKey(target - nums[i])) {
                return new int[]{hashtable.get(target - nums[i]), i};
            }
            hashtable.put(nums[i], i);
        }
        return new int[0];
        //return null;
        //throw new IllegalArgumentException("No two sum solution");
    }
}


7. 整数反转

给你一个 32 位的有符号整数 x ,返回 x 中每位上的数字反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231,  231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。 

算法一:数学方法 

反转整数的方法可以与反转字符串进行类比。

我们想重复“弹出” x 的最后一位数字,并将它“推入”到 rev 的后面。最后,rev 将与 x 相反。

要在没有辅助堆栈 / 数组的帮助下 “弹出” 和 “推入” 数字,我们可以使用数学方法。

//pop operation:
pop = x % 10;
x /= 10;

//push operation:
temp = rev * 10 + pop;
rev = temp;

但是,这种方法很危险,因为当 temp=rev⋅10+pop 时会导致溢出。

幸运的是,事先检查这个语句是否会导致溢出很容易。

为了便于解释,我们假设 rev 是正数。

 当 rev 为负时可以应用类似的逻辑。

class Solution {
    public int reverse(int x) {
        int rev = 0;
        while (x != 0) {
            int pop = x % 10;
            x /= 10;
            if (rev > Integer.MAX_VALUE/10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) return 0;
            if (rev < Integer.MIN_VALUE/10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) return 0;
            rev = rev * 10 + pop;
        }
        return rev;
    }
}

解释:

(1) int

  • 32位、有符号的以二进制补码表示的整数

  • min : -2,147,483,648(-2^31)

  • max: 2,147,483,647(2^31 - 1)

  • default: 0

  • 对应包装类:Integer

(2) 关于正负数取余运算相关疑问,参考:https://blog.csdn.net/u013094181/article/details/21863491

(3) 代码分析:

class Solution {
    public int reverse(int x) {
        int res = 0;
        while(x!=0) {
            //每次取末尾数字
            int tmp = x%10;
            //判断是否 大于 最大32位整数
            if (res>214748364 || (res==214748364 && tmp>7)) {
                return 0;
            }
            //判断是否 小于 最小32位整数
            if (res<-214748364 || (res==-214748364 && tmp<-8)) {
                return 0;
            }
            res = res*10 + tmp;//将得到的末尾数一次反转,扩大10倍
            x /= 10;//舍去尾数前面的数
        }
        return res;
    }
}    


 

算法二:字符串翻转函数

class Solution {
    public int reverse(int x) {
    String s = new StringBuilder(String.valueOf(x)).reverse().toString();
        try {
            if(s.endsWith("-")){
                return 0-Integer.valueOf(s.substring(0,s.length()-1));
            }else {
                return Integer.valueOf(s);
            }
        }catch (Exception e){
            return 0;
        }
    }
}

说明:java的String将数字转为字符串时,如果是负数,会将符号位视作单一字符一并转换;所以在对其进行字符翻转后,需要判断是否以负号结尾,从而截取数字部分;


258. 各位相加

给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。

解法一:数学上

时间复杂度为 O(1) 的解法:

对于任意一个非负整数num,反复将各个位上的数字相加,我们可以把这个整数各个位分开来看。比如一个数是525,那么就可以将它拆分成 500+20+5,对于此题,这个数对应的答案经过两次拆分,第一次:5+2+5=12;第二次:1+2=3。那么接下来,就将525这个数如何变成3的过程进行尝试:我们直接从最正确的方法入手。

首先,除个位外,每一位上的值 都是通过 (9+1) 进位的过程得到的;那么我可以发现这样一个规律:

  500%9=5;//500中有50个10,也就是第一次500mod9,可以得到50;而50中,又有5个10,那么50mod9,就最后得到个位数5;

  20%9=2;//20中有2个10,那么20mod9=2;

  520%9=7;//520中有52个10,也就是第一次520mod9,即50+20;那么70mod9=7,也就是5+2;

  525%9=3;//这时,我们已经知道520的最终余数为7;那么525mod9的值就是7+5=12;同理12mod%9=3;结束。

从以上过程,我们可以知道,任何数不断的对9取模,最终就可以得到我们想要的答案,

  • 那么按照如上的思路,似乎可以通过 n % 9 得到最后的值
  • 但是有1个关键的问题,如果 num 是 9 的倍数,那么就不适用上述逻辑。原本我是想得到 n 被打包成 10 个 1 份的份数+打不进 10 个 1 份的散落个数的和。通过与 9 取模,去获得那个不能整除的 1,作为计算份数的方式,但是如果可以被 9 整除,我就无法得到那个 1,也得不到个位上的数。
  • 所以需要做一下特殊处理,(num - 1) % 9 + 1
  • 可以这么做的原因:原本可以被完美分成 9 个为一份的 n 样物品,我故意去掉一个,那么就又可以回到上述逻辑中去得到我要的n 被打包成 10 个一份的份数+打不进 10 个一份的散落个数的和。而这个减去的 1 就相当于从,在 10 个 1 份打包的时候散落的个数中借走的,本来就不影响原来 10 个 1 份打包的份数,先拿走再放回来,都只影响散落的个数,所以没有关系。
class Solution {
    public int addDigits(int num) {
        return (num - 1) % 9 + 1;
    }
}

说明:

这道题可以 形象的看成 将num件物品,以10为单位进行分堆处理,化10倍数为个位数的操作:

  • 首先对于任意一个数,它可以第一次以10为倍数可以划分为多少块,我们不妨把每一位数分开来看,除个位外每一位数都是10的倍数,
  • 比如:521这个数,首先看最高位500,其实这个数第一次以10为单位划分等于50份,因为(10=9+1),那么用500以9为单位划分,
  • 第一次划分的余数,其实就是这个数以10位单位划分,第一次划分出来的份数,为50份;
  • 这样,就将500,以10位一个整体,变成了50,降低了进位位置,与下一级位数成为同一级别,可以进行单一位数的运算5+2,即50+20=70;
  • 同理,用70以9位单位划分,第一次划分的余数为7,那么这个7再看成以10为单位的整体,与个位上的1同级,最后7+1=8;这就最终降低成为了8份的个位数;
  • 从以上过程,我们仔细琢磨一下,它其实就是对一个数,进行除法取余数的过程:num%9的值;
  • 但是特别要注意的是,如果这个数刚好被9整除,那么余数为0,这显然不符合我们的要求;
  • 这时可以先从个位数上抽走一个数1,由以上分析,我们知道,个位数字是最后参与运算的,对分堆降级到个位数之前的运算没有任何影响,
  • 比如,990,我们先将其减1,为899:
  • 很明显990里有99个10,分成99份;99里有9个10,分成9份加个位上的9份,为18份;18里有1个10,分成1份加个位上的8份,为9份;
  • 而899里,有89个10,分成89份加个位上的9份,为98份;98里有9个10,分成9份加个位上的8份,为17份,17里有1个10,最终为8份;
  • 无论如何,我们都可以得到,num%9的值,与个位上多个1,少个1,最终的结果也会多个1和少个1,所以我们最终再加上这个1即可。

解法二

开始有点不明所以,直接用递归或者循环按照题目的意思写不就行了吗,先用递归尝试了一下。

public int addDigits(int num) {
    if (num < 10) {
        return num;
    }
    int next = 0;
    while (num != 0) {
        next = next + num % 10;
        num /= 10;
    }
    return addDigits(next);
}

没想到直接通过了,上边的递归很简单可以直接写成迭代的形式。

public int addDigits(int num) {
    while (num >= 10) {
        int next = 0;
        while (num != 0) {
            next = next + num % 10;
            num /= 10;
        }
        num = next;
    }
    return num;
}

206. 反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/pascals-triangle-ii/solution/yang-hui-san-jiao-ii-by-leetcode-solutio-shuk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

原文地址:https://www.cnblogs.com/HarryVan/p/14398136.html