21.合并两个有序链表
方法1:利用一个额外的链表
易错:
1.ListNode dummy = new ListNode(0); // 哑结点一定是0;
2. ListNode cur = dummy; //在进行遍历的时候要赋值给另外一个
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode p1 = l1,p2 = l2;
ListNode cur = dummy;
while(p1!=null&&p2!=null){
if(p1.val < p2.val){
cur.next = p1;
p1 = p1.next;
}else{
cur.next =p2;
p2 = p2.next;
}
cur = cur.next;
}
ListNode res = (p1 ==null)?p2:p1;
cur.next = res;
return dummy.next;
}
}
方法2:直接使用递归进行解决
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(l1,l2.next);
return l2;
}
}
}
26.删除排序数组的重复项
注意:第一个元素nums[0]:是直接没有动的。
class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length==0) return 0;
int i=0;
for(int j=1;j<nums.length;j++){
if(nums[i]!=nums[j]){
i++; //只有两个指针不相等的时候,才加1.所以最后的i+1能返回相应的长度。
nums[i]=nums[j]; //改变之后i之前的元素就是应该返回的不重复的数组。
}
}
return i+1;
}
}
27.移除元素
注意:这道题和上道题都是使用自身数组,替换原数组的方式,但是要注意的是i++的位置。
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;
}
}
28.实现strStr()
还有一种直接使用index的方法,感觉有点智障。
class Solution {
public int strStr(String haystack, String needle) {
int l1 = haystack.length(),l2 = needle.length();
if(l1<l2){
return -1;
}else if(l2 ==0){
return 0;
}
for(int i= 0;i<= l1-l2;i++){
if(haystack.substring(i,i+l2).equals(needle)) {
return i;
}
}
return -1;
}
}
29.两数相除
题目中要求不能用乘法和除法,但我们可以对减数进行倍增操作。对于dividend,如果dividend - divisor > 0,那么我们下一次减去的数不是divisor,而是divisor + divisor,这样倍增减数的操作可以使我们的时间复杂度到达O(log(dividend / divisor))的级别。而空间复杂度依然保持O(1)的级别。
对于最乐观的情况,假设divisor + divisor * 2 + divisor * 2 ^ 2 + divisor * 2 ^ 3 + … + divisor * 2 ^ n = dividend。那么我们总共只进行了n次操作,这个时间复杂度显然就是O(log(dividend / divisor))。而如果divisor + divisor * 2 + divisor * 2 ^ 2 + divisor * 2 ^ 3 + … + divisor * 2 ^ n > dividend,但是divisor + divisor * 2 + divisor * 2 ^ 2 + divisor * 2 ^ 3 + … + divisor * 2 ^ (n - 1) < dividend,那么我们就要以dividend - (divisor + divisor * 2 + divisor * 2 ^ 2 + divisor * 2 ^ 3 + … + divisor * 2 ^ (n - 1))为被减数进行重复的操作,即从divisor开始减起。直至我们的被减数小于divisor为止。
而对于结果的值,我们需要根据flag还有是否越界来讨论清楚各种情况。
public class Solution {
public int divide(int dividend, int divisor) {
int sign =1;
//if((dividend>0)&&(divisor<0) || (dividend<0)&&(divisor>0)) sign = -1;
if((dividend>0)^(divisor>0)) sign = -1; //考虑结果是负数
long ldividend =Math.abs((long)dividend);
long ldivisior = Math.abs((long)divisor); //考虑越界的问题
if(ldivisior > ldividend || ldividend == 0) return 0;//这里不用考虑除数为0的情况,因为Java中自动会报异常。
long lres = divide(ldividend,ldivisior);
int res =0;
if(lres > Integer.MAX_VALUE){ //如果结果越界之后
res = (sign ==1 )?Integer.MAX_VALUE:Integer.MIN_VALUE;
//res = (sign ==1 )?Integer.MAX_VALUE:Integer.MIN_VALUE;
}else res =(int)(sign*lres);
return res;
}
public long divide(long ldividend,long ldivisior){
if(ldividend < ldivisior) return 0;
long sum =ldivisior;
long multiple =1; //表示的是倍数
while((sum+sum) <= ldividend){
sum += sum;
multiple +=multiple;
}
return multiple + divide(ldividend-sum,ldivisior);
}
}
30.串联所有单词的子串
没有弄清楚为啥在每次遍历的时候需要重新copy一次map。
—其实防止,万一在一次循环中,只有一个单词匹配的时候,如果不重新copy的话,就会影响原来的map中key值所对应的次数。。
map.getOrDefault()
意思就是当Map集合中有这个key时,就使用这个key值,如果没有就使用默认值defaultValue
public static List<Integer> findSubstring(String s, String[] words) {
if(s == null || words ==null) return new ArrayList<>();
List<Integer> res = new ArrayList<>();
int n = words.length;
int m = words[0].length();
HashMap<String,Integer> map = new HashMap<String,Integer>();
for(String ch:words){
map.put(ch,map.getOrDefault(ch,0)+1);
}
for(int i = 0;i <= s.length() - n*m;i++){
HashMap<String,Integer> copy = new HashMap<String,Integer>(map);
int k =n,j =i;
while(k> 0){
String ans = s.substring(j,j+m);
if(!copy.containsKey(ans) || copy.get(ans) < 1){
break;
}
copy.put(ans,copy.get(ans)-1);
k--;
j+=m;
}
if(k==0) res.add(i);
}
return res;
}
31.下一个全排列
思路参考博文,点击这里
需要注意的是:if(i >=0)的判断,如果没有找到逆序的情况,直接进行翻转。
class Solution {
public void nextPermutation(int[] nums) {
if(nums == null || nums.length == 0){
return ;
}
int i = nums.length - 2;
while(i >=0 && nums[i] >= nums[i+1]){ //找出第一个不是逆序排列的数的位置i
i--;
}
if(i >= 0){ //这种是表示的是找到了逆序排列,下面进行交换
int j = nums.length -1;
while(j >= 0 && nums[j] <= nums[i]){ //找出逆序中第一个比i大的位置j
j--;
}
swap(nums,i,j);
}
reverse(nums,i+1,nums.length-1); //(1)如果没有找到的话,i=-1,同时进行全部交换就行了。(2)如果找到了,后面的进行交换
}
public void reverse(int[] nums,int start, int end){
int i = start,j = end;
while(i < j){
swap(nums,i++,j--);
}
}
public void swap(int[] nums,int i ,int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
36. 有效的数独
题目详解
注意:字符和数字之间的相互转换:数字 = 字符 -‘0’:例子:数字1的ASCII码为49,而0的ASCII码为48,所以正好能计算。
getOrDefault(n,0)
:表示如果HashMap中有值就使用HashMap中的value值,如果没有的话使用默认的值。
注意:
1.时间复杂度和空间复杂度的分析:都是O(1)
2.对于HashMap数组建立之后,每个数组要放入一个HashMap进去。
注意:建立HashMap数组之后,如果不进行第二步的赋值操作的话,row里面的元素是null,所以必须进行一个初始化,不然会报空指针异常。
class Solution {
public boolean isValidSudoku(char[][] board) {
// init data
HashMap<Integer, Integer> [] rows = new HashMap[9];
HashMap<Integer, Integer> [] columns = new HashMap[9];
HashMap<Integer, Integer> [] boxes = new HashMap[9];
for (int i = 0; i < 9; i++) {
rows[i] = new HashMap<Integer, Integer>();
columns[i] = new HashMap<Integer, Integer>();
boxes[i] = new HashMap<Integer, Integer>();
}
// validate a board
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char num = board[i][j];
//题目中空白格用'.'进行表示。
if (num != '.') {
int n = (int)num;
int box_index = (i / 3 ) * 3 + j / 3;
// keep the current cell value
rows[i].put(n, rows[i].getOrDefault(n, 0) + 1);
columns[j].put(n, columns[j].getOrDefault(n, 0) + 1);
boxes[box_index].put(n, boxes[box_index].getOrDefault(n, 0) + 1);
// check if this value has been already seen before
if (rows[i].get(n) > 1 || columns[j].get(n) > 1 || boxes[box_index].get(n) > 1)
return false;
}
}
}
return true;
}
}
37解数独
为了便于采用:二维数组的一个坐标系。所以此题采用的是先由上向下进行遍历,然后在从左到有进行遍历。
class Solution {
private boolean[][] rows = new boolean[9][10];
private boolean[][] cols = new boolean[9][10];
private boolean[][] boxes = new boolean[9][10];
public void solveSudoku(char[][] board) {
for (int j = 0; j < 9; j++) {
for (int i = 0; i < 9; i++) {
if (board[i][j] != '.') {
// n : 1 - 9
int num = board[i][j] - '0';
// 在第i行 n 被用到
rows[i][num] = true;
// 在第j列 n 被用到
cols[j][num] = true;
// 在小格子中 n 被用到
boxes[(i / 3) * 3 + j / 3][num] = true;
}
}
}
dfs(board, 0, 0);
}
private boolean dfs(char[][] board, int x, int y) {
// 行数等于9的时候找到了一组解
if (y == 9) return true;
// 下一个位置,从左到右,从上到下
int nx = (x + 1) % 9;
int ny = (nx == 0) ? y + 1 : y;
// 当前位置已经是数字,看下一个位置
if (board[x][y] != '.') return dfs(board, nx, ny);
// 当前位置需要填一个数字:1-9
for (int i = 1; i <= 9; i++) {
// 小格子的位置
int boxIndex = (x / 3) * 3 + y / 3;
// 都是false的时候放置当前位置放 i
if (!(rows[x][i] || cols[y][i] || boxes[boxIndex][i])) {
rows[x][i] = true;
cols[y][i] = true;
boxes[boxIndex][i] = true;
board[x][y] = (char) (i + '0');
if (dfs(board, nx, ny)) return true;
board[x][y] = '.';
boxes[boxIndex][i] = false;
cols[y][i] = false;
rows[x][i] = false;
}
}
return false;
}
}
38. 外观数列
注意:第二个循环从1开始,是因为在大循环之中,已经取了第0个了,所以这里直接从1开始。
/**
* 解题思路:
* 本题的难点在于:报数的概念理解,至少我从题意中没有很清晰的理解,但是感觉像是个递推式
* 从4->5分析,将4个每一位拆开看(个数+数字),4=1211 => 1=11,2=12,11=21,所以5=111221
* 所以解题用循环,从1->n可求解出来
*
* @param n
* @return
*/
public String countAndSay(int n) {
String str = "1";
for (int i = 2; i <= n; i++) {
StringBuilder builder = new StringBuilder();
char pre = str.charAt(0);
int count = 1;
for (int j = 1; j < str.length(); j++) {
char c = str.charAt(j);
if (c == pre) {
count++;
} else {
builder.append(count).append(pre);
pre = c;
count = 1;
}
}
builder.append(count).append(pre);
str = builder.toString();
}
return str;
}
动态规划
class Solution {
public String countAndSay(int n) {
String[] dp = new String[n];
dp[0] = "1";
for(int i = 1;i < n;i++){
int last = dp[i-1].length();
StringBuilder sb = new StringBuilder();
for(int j =0;j < last;j++){
int k = j;
while(k < last-1 && dp[i-1].charAt(k) == dp[i-1].charAt(k+1)){
k++;
}
char ch = dp[i-1].charAt(j);
sb.append(k -j +1 + "").append(ch);
j = k;
}
dp[i] = sb.toString();
}
return dp[n-1];
}
}