4.10之前的刷题记录

  1. 二维数组的查找

    • 描述:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

    • 错误思路:第一列从上往下依次比较,找到比要寻找整数大的的上一层,进行遍历

    • 错误反省:错将条件当成每一行的起始都比之前的大

    • 解题思路:从左下角开始与目标元素进行对比,若目标元素大,列数加一;若目标元素小,行数减一

    • 题解

      public class Solution {
          public boolean Find(int target, int [][] array) {
              if(array.length==0)
              {
                  return false;   
              }
              int i=0;j=array.length-1;
              while(i<array[0].length&&j>=0)
              {
                  if(target==array[i][j])
                  {
                      return true;
                  }    
                  else if(target>array[i][j])
                  {
                      i++;
                  }
                  else
                  {
                      j--;
                  }
              }
              return false;
          }
      }
      
    • 扩展题:序号2

  2. 回文数组

    • 描述:对于一个给定的正整数组成的数组 a[] ,如果将 a 倒序后数字的排列与 a 完全相同,我们称这个数组为“回文”的。

      例如, [1, 2, 3, 2, 1] 的倒序是他自己,所以是一个回文的数组;而 [1, 2, 3, 1, 2] 的倒序是 [2, 1, 3, 2, 1] ,所以不是一个回文的数组。

      对于任意一个正整数数组,如果我们向其中某些特定的位置插入一些正整数,那么我们总是能构造出一个回文的数组。

      输入一个正整数组成的数组,要求你插入一些数字,使其变为回文的数组,且数组中所有数字的和尽可能小。输出这个插入后数组中元素的和。

    • 思路:从开始的数组两边进行比较,直至两个指针相等或前面的指针大于后面的指针,指针所指向的元素如果不相等,添加较小的元素,如果较小的元素在后面,除移动以后元素外无其他操作,若较小元素在前面,较大的指针需要加一,然后两个指针同时减一,最后计算数组之和

    • 解决算法:动态规划-3.1看算法导论的二叉搜索树,红黑树,b树,动态规划

  3. 删除字符串中的空格-字符串

    • 描述:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

    • 思路:

      1. 使用java自带的toString.replace(" “,”%20")
      2. 从后往前遍历,遇见空格把之后的元素向后移动两个位置,然后赋值,但这个算法的时间复杂度较高
      3. 创建一个新的字符串
    • 题解

      public class Solution {
          public String replaceSpace(StringBuffer str) {
              nowlength=str.length()-1;
          	for(int j=str.length()-1;j>=0;j--)
              {
                  if(str.charAt(j)==' ')
                  {
                      nowlength=nowlength+2;
                      str.setLength(nowlength+1);
                      for(int i=nowlength;i>j+2;i--)
                      {
                          str.setCharAt(i,str.charAt(i+2))
                      }
                      str.setCharAt(j,'%');
                      str.setCharAt(j+1,'2');
                      str.setCharAt(j+2,'0');
                      j=j+2;
                  }
              }
              return str.toString();
          }
      }
      
  4. 从尾到头打印链表

    • 描述:输入一个链表,按链表从尾到头的顺序返回一个ArrayList

    • 思路1:可以先利用链表中的元素创建数组,然后对数组实行reverse方法

    • 题解1:

      class Solution {
      public:
          vector<int> printListFromTailToHead(ListNode* head) {
              vector<int> A;
              while(head)
              {
                  A.push_back(head->val);
                  head=head->next;
              }
              reverse(A.begin(),A.end());
              return A;
          }
      };
      
    • 思路2:通过头插法建立新链表,然后再利用指针遍历输出

  5. 重建二叉树

    • 描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回

    • 思路:递归,前序序列的第一个元素在中序序列中找出,划分成左子树和右子树,然后分别对左右子树再使用重建二叉树的函数

    • 题解:

      import java.util.*;
      
      public class Solution {
          public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
              if(pre.length==0||in.length==0)
              {
                  return null;
              }
              TreeNode root=new TreeNode(pre[0]);
              for(int i=0;i<in.length;i++)
              {
                  if(in[i]==pre[0])
                  {
                      root.left=reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));
                root.right=reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length));
                  }
              }
               return root;
              
          }
      }
      

      来比较一下c++和java版本的区别,区别就在于递归的时候java直接生成了数组,c++用vector循环遍历生成新数组。

      class Solution {
      public:
          TreeNode* reConstructBinaryTree(vector pre,vector vin) {
              int vinlen=vin.size();
              if(vinlen==0)
                  return NULL;
              vector pre_left, pre_right, vin_left, vin_right;
              //创建根节点,根节点肯定是前序遍历的第一个数
              TreeNode* head = new TreeNode(pre[0]);
              //找到中序遍历根节点所在位置,存放于变量gen中
              int gen=0;
              for(int i=0;i<vinlen;i++){
                  if(vin[i]==pre[0]){
                      gen=i;
                      break;
                  }
              }
              //对于中序遍历,根节点左边的节点位于二叉树的左边,根节点右边的节点位于二叉树的右边
              // 左子树
              for(int i = 0; i < gen; i++){
                  vin_left.push_back(vin[i]);
                  pre_left.push_back(pre[i+1]);//先序第一个为根节点
              }
              // 右子树
              for(int i = gen + 1; i < vinlen; i++){
                  vin_right.push_back(vin[i]);
                  pre_right.push_back(pre[i]);
              }
              //递归,执行上述步骤,区分子树的左、右子子树,直到叶节点
              head->left = reConstructBinaryTree(pre_left, vin_left);
              head->right = reConstructBinaryTree(pre_right, vin_right);
              return head;
          }
      };
      
  6. 用两个栈实现队列

    • 描述:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型

    • 思路:栈是先进后出,队列是先进先出,我们利用两个栈,将第一个栈的顺序从栈底到栈顶是按照压入的顺序,其pop操作原样即可

    • 题解:

      import java.util.Stack;
      
      public class Solution {
          Stack<Integer> stack1 = new Stack<Integer>();
          Stack<Integer> stack2 = new Stack<Integer>();
          
          public void push(int node) {
             while(!stack1.empty())
             {
                 stack2.push(stack1.pop());
             }
             stack1.push(node);
             while(!stack2.empty())
             {
                 stack1.push(stack2.pop());
             }
          }
          
          public int pop() {
              return stack1.pop();
          }
      }
      
  7. 旋转数组的最小数字

    • 描述:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0

    • 思路:既然是非递减数组的旋转,我们要利用这个特性。如果不利用这个特性的话,可以用一个变量存储最小值,遍历整个数组进行比较;或者对此数组进行排序,排序后取第一个元素,当然,这两种都没有利用到旋转数组的特性。我们可以从前往后比较,遇见比前一个元素小的,即是最小元素

    • 题解:

      import java.util.ArrayList;
      public class Solution {
          public int minNumberInRotateArray(int [] array) {
              if(array.length==0)
              {
                  return 0;
              }
              int min=0;
              for(int i=1;i<array.length;i++)
              {
                  if(array[i]<array[i-1])
                  {
                      min=array[i];
                  }
              }
              return min;
          }
      }
      
  8. 费波拉契数列

    • 描述:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)

    • 思路:此题考查递归于循环,将问题分解成规模较小的问题

    • 题解:

      public class Solution {
          public int Fibonacci(int n) {
              if(n>39)
              {
                  return -1;
              }
              else if(n==0)
              {
                  return 0;
              }
              else if(n==1)
              {
                  return 1;
              }
              else
              {
                  return Fibonacci(n-1)+Fibonacci(n-2);
              }
          }
      }
      

      但是这样的算法效率并不高,我们可以用一个数组来存储计算过的值,这实际上是一维的动态规划。

  9. 后序遍历的非递归处理方法

    • 描述:对于一二叉树,我们要用非递归的方式去解决其后序遍历问题
    • 思路:我们用两个栈解决问题,首先把二叉树的根节点压入其中栈1中,然后弹出压入到栈2,如果无左右节点则不进行任何操作,存在左右节点的话,则需要将左右节点依次压入栈1,再弹出压入到栈2,直到栈1空,弹出栈2中所有元素即可获得遍历序列
  10. 青蛙跳台阶问题

    • 描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)

    • 思路:这题我们采用动态规划的算法,即跳n级台阶可以分为最后一步是1级加上前面跳n-1级台阶,和最后一步是两级加上前面跳n-2级台阶,这题类似于钢条切割问题,即res(n)=res(n-1)+res(n-2)

    • 题解:

      public class Solution {
          public int JumpFloor(int target) {
              if(target==1)
              {
                  return 1;
              }
              else if(target==2)
              {
                  return 2;
              }
              else
              {
                  return JumpFloor(target-1)+JumpFloor(target-2);
              }
          }
      }
      
  11. leetcode分发糖果问题-贪心算法

    • 描述:老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。你需要按照以下要求,帮助老师给这些孩子分发糖果:

      1. 每个孩子至少分配到 1 个糖果。

      2. 相邻的孩子中,评分高的孩子必须获得更多的糖果。

        那么这样下来,老师至少需要准备多少颗糖果呢?

    • 错误思路:首先从左往右遍历,第一个孩子给一个糖果,,遇见比自己分数高的,加一个糖果,如若不然重置为一,遍历完后从右向左遍历,类似操作

    • 错误原因:

      1. 错误点:不能在从右向左遍历的过程中将糖果数重置成1
      2. 解决方法:只在从左向右的过程中重置为1
    • 题解:

      class Solution {
          public int candy(int[] ratings) {
              int[] res=new int[ratings.length];
              res[0]=1;
              for(int i=1;i<ratings.length;i++)
              {
                  if(ratings[i]>ratings[i-1])
                  {
                      res[i]=res[i-1]+1;
                  }
                  else
                  {
                      res[i]=1;
                  }
              }
              for(int k=ratings.length-2;k>=0;k--)
              {
                  if(ratings[k]>ratings[k+1])
                  {
                      if(res[k]>res[k+1]+1)
                      {
                          res[k]=res[k];
                      }
                      else
                      {
                          res[k]=res[k+1]+1;
                      }
                  }
              }
              int resl=0;
              for(int j=0;j<res.length;j++)
              {
                  resl+=res[j];
              }
              return resl;
          }
      }
      
  12. 变态青蛙跳台阶

    • 描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法

    • 思路:类似于斐波拉契数列,只是我们把情况变成了n种

    • 题解:

      public class Solution {
          public int JumpFloorII(int target) {
              int i=1;
              int res=0;
              while(i!=target)
              {
                  res+=JumpFloorII(target-i);
              }
              res=res+1;
              return res;
          }
      }
      

      但是这种算法太过繁琐,无法在规定时间内跑完,所以我们换一种思路

    • 思路2:直接用一个数组存取之前的结果,然后遍历相加即可

    • 题解2:

      public class Solution {
          public int JumpFloorII(int target) {
              if(target<2)
              {
                  return target;
              }
              else
              {
                  int[] res=new int[target+1];
                  for(int j=0;j<target+1;j++)
                  {
                      res[j]=1;
                  }
                  res[0]=0;
                  for(int i=0;i<target;i++)
                  {
                      for(int k=0;k<i;k++)
                      {
                          res[i]+=res[k];
                      }
                  }
                  for(int m=0;m<target;m++)
                  {
                      res[target]+=res[m];
                  }
                  return res[target];
              }
          }
      }
      
  13. 矩形覆盖-简单动态规划问题(已解决·,不记)

  14. 二进制中1的个数

    • 描述:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示

    • 特殊思路:一个整数减一后,其最右边的1会变成0,而这个1右面的0会全部变成1,我们把这个数和原来的整数做与运算,则包括其最右边的1的右边全部变成0,这个整数有多少个1,就可以做多少次这样的运算

    • 题解:

      public class Solution {
          public int NumberOf1(int n) {
              int count=0;
              while(n!=0)
              {
                  n=n&(n-1);
                  count++;
              }
              return count;
          }
      }
      
    • 一般思路:用一个标志变量flag,其初始值为1,不停的左移1位并与n做与运算,若不为0,则计数器加一,直至flag为0

    • 题解:

      public class Solution {
          public int NumberOf1(int n) {
              int num=0;
              int flag=1;
              while(flag!=0)
              {
                  if((n&flag)!=0)
                      num++;
                  flag<<=1;
              }
              return num;
          }
      }
      
  15. 数值的整数次方

    • 描述:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方,保证base和exponent不同时为0

    • 思路1:直接连续累乘,然后区分整数是负数还是正数即可

    • 题解1:

      public class Solution {
          public double Power(double base, int exponent) {
              int exponent2;
              if(exponent<0)
              {
                  exponent2=-exponent;
              }
              else
              {
                  exponent2=exponent;
              }
              if(exponent2==0)
              {
                  return 1.0;
              }
              double res=1.0;
              while(exponent2!=0)
              {
                  res*=base;
                  exponent2--;
              }
              if(exponent>0)
              {
                  return res;
              }
              else
              {
                  return 1.0/res;
              }
        }
      }
      
    • 思路2:快速幂乘法-之后我们论断的数据全是base的指数,我们可以将n用2i的累加式来表示,n的二进制表示中对应位数上的数字即为2i的系数,n每右移一位,base通过自乘运算达到平方,此算法的复杂度为O(logn)

    • 题解2:

      public class Solution {
          public double Power(double base, int exponent) {
              int exponent2;
              if(exponent<0)
              {
                  exponent2=-exponent;
              }
              else
              {
                  exponent2=exponent;
              }
              if(exponent2==0)
              {
                  return 1.0;
              }
              double res=1.0;
              while(n!=0)
              {
                  if(n&1)
                  {
                      res*=base;
                  }
                  base*=base;
                  n>>=1;
              }
              if(exponent>0)
              {
                  return res;
              }
              else
              {
                  return 1.0/res;
              }
        }
      }
      
  16. 调整数组顺序使得奇数位于偶数前面、

    • 描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变

    • 思路1:比较暴力,用两个数组,分别用来存奇数和偶数,对原数组进行遍历,分别放置后重新对原数组赋值

    • 题解1:

      public class Solution {
          public void reOrderArray(int [] array) {
              int[] array1=new int[10];
              int[] array2=new int[10];
              int num1=0;
              int num2=0;
              for(int i=0;i<array.length;i++)
              {
                  if((array[i]%2)!=0)
                  {
                      array1[num1]=array[i];
                      num1++;
                  }
                  else
                  {
                      array2[num2]=array[i];
                      num2++;
                  }
              }
              for(int j=0;j<array.length;j++)
              {
                  if(j>=num1)
                  {
                      array[j]=array2[j-num1];
                  }
                  else
                  {
                      array[j]=array1[j];
                  }
              }
          }
      }
      

      反省:这个算法一直报错,让我纠结了很长时间,因为按理来说这个算法是最简单直接的,但是出现了数组访问越界的错误,我怎么也找不到在哪越界的,后来发现是在计算新数组大小num1和num2的时候多加了1,导致超出数组边界,下次一定要引以为戒。

  17. 输出单链表的倒数第k个元素

    • 描述:输入一个链表,输出该链表中倒数第k个节点

    • 思路:用两个指针,一个是fast指针,一个是slow指针,fast指针先移动k步,然后slow指针和fast指针同时移动,至fast指针为null时返回slow指针所指的节点

    • 题解:

      /*
      public class ListNode {
          int val;
          ListNode next = null;
      
          ListNode(int val) {
              this.val = val;
          }
      }*/
      public class Solution {
          public ListNode FindKthToTail(ListNode head,int k) {
              ListNode fast=head;
              ListNode slow=head;
              while(k!=0)
              {
                  if(fast==null)
                  {
                      return null;
                  }
                  fast=fast.next;
                  k--;
              }
              while(fast!=null)
              {
                  fast=fast.next;
                  slow=slow.next;
              }
              return slow;
          }
      }
      

      这个测试的时候报了nullpointer的错误,他的测试用例里面用了一个空的链表,所以如果不在移动k步的时候进行空节点检测,就会导致这种错误。

    • 正确但是麻烦的思路:我们首先遍历一遍原始单链表,根据头插法创建原始单链表的reverse,然后找到其第k个元素

    • 错误题解:

      /*
      public class ListNode {
          int val;
          ListNode next = null;
      
          ListNode(int val) {
              this.val = val;
          }
      }*/
      public class Solution {
          public ListNode FindKthToTail(ListNode head,int k) {
              ListNode newhead=new ListNode(0);
              ListNode overall=head;
              while(overall!=null)
              {
                  ListNode newoverall=overall;
                  if(newhead.next==null)
                  {
                      newhead.next=newoverall;
                  }
                  else
                  {
                      newoverall.next=newhead.next;
                      newhead.next=newoverall;
                  }
                  overall=overall.next;
              }
              ListNode newoverall2=newhead;
              while(k!=0)
              {
                  if(newoverall2==null)
                  {
                      return null;
                  }
                  newoverall2=newoverall2.next;
                  k--;
              }
              return newoverall2;
          }
      }
      
    • 关于这题的思考:之前一直不明白为什么单链表需要一个不存任何值的头节点,此题中给出的链表不存在头节点,在用头插法建立新链表的时候十分不方便,更新一下,这题的链表是有头节点的,再次更新一下,无头节点。但是这种做法是存在缺陷的,一旦要求输出有k的元素的单链表的第k个节点,会导致输出null

    • 思路3:思路2会创建一个新的链表,造成空间复杂度的上升,我们实际上可以在原有链表的基础上进行翻转,反转的思路其实就是将指向下一个节点的指针域指向前一个节点。

    • 题解3:

      /*
      public class ListNode {
          int val;
          ListNode next = null;
      
          ListNode(int val) {
              this.val = val;
          }
      }*/
      public class Solution {
          public ListNode FindKthToTail(ListNode head,int k) {
              ListNode pre=head.next;
              ListNode cur=head.next;
              ListNode next=cur.next;
              cur.next=null;
              cur=next;
              while(cur!=null)
              {
                  cur.next=pre;
                  pre=cur;
                  cur=next;
              }
              head.next=pre;
              ListNode res=head;
              while(k!=0)
              {
                  if(res==null)
                  {
                      return null;
                  }
                  else
                  {
                      res=res.next;
                      k--;
                  }
              }
              return res;
          }
      }
      

      注:关于单链表的反转和头插法操作有待加强

  18. 反转链表

    • 描述:输入一个链表,反转链表后,输出新的链表的表头

    • 思路:17题思路三已经很好的阐释了

    • 题解:

      /*
      public class ListNode {
          int val;
          ListNode next = null;
      
          ListNode(int val) {
              this.val = val;
          }
      }*/
      public class Solution {
          public ListNode ReverseList(ListNode head) {
              if(head==null||head.next==null)
              {
                  return head;
              }
              ListNode pre=null;
              ListNode cur=head;
              ListNode next=null;
              while(cur!=null)
              {
                  next=cur.next;
                  cur.next=pre;
                  pre=cur;
                  cur=next;
              }
              return pre;
          }
      }
      
  19. 合并两个单调递增的链表

    • 描述:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则

    • 思路:循环遍历至其中一个为空指针,将剩下的接在新链表的后面

    • 题解:

      /*
      public class ListNode {
          int val;
          ListNode next = null;
      
          ListNode(int val) {
              this.val = val;
          }
      }*/
      public class Solution {
          public ListNode Merge(ListNode list1,ListNode list2) {
              ListNode newlist=new ListNode(0);
              ListNode newList=newlist;
              while(list1!=null&&list2!=null)
              {
                  if(list1.val<=list2.val)
                  {
                      newList.next=list1;
                      list1=list1.next;
                  }
                  else
                  {
                      newList.next=list2;
                      list2=list2.next;
                  }
                  newList=newList.next;
              }
              if(list1==null)
              {
                  while(list2!=null)
                  {
                      newList.next=list2;
                      list2=list2.next;
                      newList=newList.next;
                  }
              }
              else
              {
                      newList.next=list1;
                      list1=list1.next;
              }
              return newlist.next;
          }
      }
      
  20. 树的子结构

    • 描述:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

    • 思路:

      1. 首先判断B树是否为空,为空的话返回false
      2. 我们用先序遍历的方式去扫描两棵树,如果不用栈,我们就用递归的方式去解决
    • 题解:

      
      
  21. 两数相加

    • 描述:给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

      如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

      您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

    • 思路:模拟小学数学的加法进位,因为是逆序存储,所以不用反转

    • 题解:

      /**
       * Definition for singly-linked list.
       * public class ListNode {
       *     int val;
       *     ListNode next;
       *     ListNode(int x) { val = x; }
       * }
       */
      class Solution {
          public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
              ListNode pre=new ListNode(0);
              ListNode cur=pre;
              int carry=0;
              while(l1!=null||l2!=null)
              {
                  int x=l1==null?0:l1.val;
                  int y=l2==null?0:l2.val;
                  int sum=x+y+carry;
                  ListNode node=new ListNode(sum%10);
                  cur.next=node;
                  cur=node;
                  carry=sum/10;
                  if(l1!=null)
                  {
                      l1=l1.next;
                  }
                  if(l2!=null)
                  {
                      l2=l2.next;
                  }
              }
              if(carry==1)
              {
                  ListNode node=new ListNode(carry);
                  cur.next=node;
                  cur=node;
              }
              return pre.next;
          }
      }
      

  22. 删除链表的倒数第N个节点

    • 描述:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

    • 思路:这题与之前的找到第倒数N个节点不同就是要删除元素

    • 题解:

      /**
       * Definition for singly-linked list.
       * public class ListNode {
       *     int val;
       *     ListNode next;
       *     ListNode(int x) { val = x; }
       * }
       */
      class Solution {
          public ListNode removeNthFromEnd(ListNode head, int n) {
              ListNode fast=head;
              ListNode slow=head;
              ListNode pre=head;
              while(n!=0)
              {
                  if(fast==null)
                  {
                      return null;
                  }
                  fast=fast.next;
                  n--;
              }
              while(fast!=null)
              {
                  fast=fast.next;
                  pre=slow;
                  slow=slow.next;
              }
              if(pre==slow&&pre==head)
              {
                  return head.next;
              }
              else
              {
                  pre.next=slow.next;
              }
              return head;
          }
      }
      
  23. 合并k个有序链表

    • 描述:输入链表数组,合并k个有序链表

    • 思路:用类似合并两个数组的办法,多指针并起

    • 自己的题解:

      /**
       * Definition for singly-linked list.
       * public class ListNode {
       *     int val;
       *     ListNode next;
       *     ListNode(int x) { val = x; }
       * }
       */
      class Solution {
          public ListNode mergeKLists(ListNode[] lists) {
              int flag=1;
              ListNode res=new ListNode(0);
              ListNode temp=res;
              while(flag!=0)
              {
                  int[] value=new int[lists.length];
                  for(int j=0;j<lists.length;j++)
                  {
                      value[j]=lists[j]==null?-1:lists[j].val;
                  }
                  int min=value[0];
                  int num=0;
                  for(int i=0;i<lists.length;i++)
                  {
                      if(value[i]<min)
                      {
                          num=i;
                          min=value[i];
                      }
                  }
                  ListNode node=new ListNode(min);
                  temp.next=node;
                  temp=ndoe;
                  lists[num]=lists[num].next;
                  for(int k=0;k<lists.length;k++)
                  {
                      if(lists[k]==null)
                      {
                          flag=0;
                      }
                  }
              } 
              return res.next;      
          }
      }
      

      这算法解决的大部分的问题,在遇见超大数据输入的时候超出了时间限制

    • 思路2:两个两个的合并,共合并n-1次

    • 题解2:

      /**
       * Definition for singly-linked list.
       * public class ListNode {
       *     int val;
       *     ListNode next;
       *     ListNode(int x) { val = x; }
       * }
       */
      class Solution {
          public ListNode mergeKLists(ListNode[] lists) {
              if(lists.length==0)
              {
                  return null;
              }
              if(lists.length==1)
              {
                  return lists[0];
              }
              else
              {
                  ListNode newnode=mergetTwoLists(lists[0],lists[1]);
                  for(int k=2;k<lists.length;k++)
                  {
                      newnode=mergetTwoLists(newnode,lists[k]);
                  }
                  return newnode;
              }
          }
          public ListNode mergetTwoLists(ListNode l1,ListNode l2){
              ListNode res=new ListNode(0);
              ListNode temp=res;
              while(l1!=null||l2!=null)
              {
                  int x=l1==null?1000:l1.val;
                  int y=l2==null?1000:l2.val;
                  if(x<=y)
                  {
                      ListNode node=new ListNode(x);
                      temp.next=node;
                      temp=node;
                      if(l1!=null)
                      {
                          l1=l1.next;
                      }
                  }
                  else
                  {
                      ListNode node=new ListNode(y);
                      temp.next=node;
                      temp=node;
                      if(l2!=null)
                      {
                          l2=l2.next;
                      }
                  }
              }
              return res.next;
          }
      }
      

      此算法通过了所有的案例,但是用时和空间复杂度仍然比较高


  24. 树的子树(leetcode)

    • 描述:输入两棵二叉树A,B,判断B是不是A的子树

    • 思路:一棵树是另外一颗树的子树,只有三种情况。分别是与这棵树,与这棵树的左子树,与这棵树的右子树相等,我们再调用一个相等函数即可判断。

    • 题解:

      /**
      public class TreeNode {
          int val = 0;
          TreeNode left = null;
          TreeNode right = null;
      
          public TreeNode(int val) {
              this.val = val;
      
          }
      
      }
      */
      public class Solution {
          public boolean HasSubtree(TreeNode root1,TreeNode root2) {
              
          }
          public boolean IsSubtree(TreeNode root1,TreeNode root2){
              if(root2==null&&root1==null)
              {
                  return true;
              }
              if((root2==null&&root1!=null)||(root1==null&&root2!=null))
              {
                  return false;
              }
              if(root1.val!=root2.val)
              {
                  return false;
              }
              else
              {
                  return IsSubtree(root1.left,root2)&&IsSubtree(root1.right,root2);
              }
          }
      }
      
  25. 树的子结构

    • 描述:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

    • 思路:子结构是比子树更宽松的一种关系,但是子结构可能是他的1子树,也可能是他子树的子树,所以我们只需要一个函数来判断是否是子树即可,然后递归调用,注意,子结构不一定要到叶节点,所以调用函数IsSubtree的时候如果后面的参数是null,返回的是true

    • 题解:

      /**
      public class TreeNode {
        int val = 0;
          TreeNode left = null;
          TreeNode right = null;
      
          public TreeNode(int val) {
              this.val = val;
      
          }
      
      }
      */
      public class Solution {
          public boolean HasSubtree(TreeNode root1,TreeNode root2) {
              if(root1==null||root2==null)
              {
                  return false;
              }
              else{
                  return IsSubtree(root1,root2)||HasSubtree(root1.left,root2)||HasSubtree(root1.right,root2);
              }
          }
          public boolean IsSubtree(TreeNode root1,TreeNode root2){
              if(root2==null)
              {
                  return true;
              }
              if(root1==null)
              {
                  return false;
              }
              else
              {
                  if(root1.val!=root2.val)
                  {
                      return false;
                  }
                  return IsSubtree(root1.left,root2.left)&&IsSubtree(root1.right,root2.right);
              }
          }
      }
      
  26. 二叉树的镜像(已解决,简单,不记)

  27. 顺时针打印矩阵

    • 描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

    • 思路:用四个整型变量控制上下左右的边界,每遍历一行或一列,将对应的边界删除,同时检测边界的合理性,如右边大于等于左边,若出现不合理情况,则跳出循环

    • 题解:

      import java.util.ArrayList;
      public class Solution {
          public ArrayList<Integer> printMatrix(int [][] matrix) {
              ArrayList<Integer> res=new ArrayList<>();
              if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
                  return res;
              }
              int up=0;
              int down=matrix.length-1;
              int left=0;
              int right=matrix[0].length-1;
              while(true)
              {
                  for(int i=left;i<=right;i++)
                  {
                      res.add(matrix[up][i]);
                  }
                  up++;
                  if(up>down)
                  {
                      break;
                  }
                  for(int j=up;j<=down;j++)
                  {
                      res.add(matrix[j][right]);
                  }
                  right--;
                  if(right<left)
                  {
                      break;
                  }
                  for(int m=right;m>=left;m--)
                  {
                      res.add(matrix[down][m]);
                  }
                  down--;
                  if(down<up)
                  {
                      break;
                  }
                  for(int n=down;n>=up;n--)
                  {
                      res.add(matrix[n][left]);
                  }
                  left++;
                  if(left>right)
                  {
                      break;
                  }
              }
              return res;
          }
      }
      
  28. 包含min函数的栈

    • 描述:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

      注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。

    • 思路:要想得到栈中所含的最小元素,且要求时间复杂度为O(1),使用两个栈,一个存放全部的元素,另一个存放每次压入新元素时的最小值,min函数对存放最小值的栈进行top操作即可。

    • 题解:

      import java.util.Stack;
      
      public class Solution {
          private static int mininum=10000;
          private static Stack<Integer> stacktotal = new Stack<Integer>();
          private static Stack<Integer> stackmin = new Stack<Integer>();
      
          public void push(int node) {
              if(node<mininum)
              {
                  mininum=node;
              }
              stacktotal.push(node);
              stackmin.push(mininum);
          }
          
          public void pop() {
              if(stacktotal.size()>0)
              {
                  stacktotal.pop();
                  stackmin.pop();
              }
          }
          
          public int top() {
              return stacktotal.peek();
          }
          
          public int min() {
              return stackmin.peek();
          }
      }
      
  29. 栈的压入,弹出序列

    • 描述:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

    • 思路:将pushA数组压入栈,每次压入后检测与popA数组内元素是否相同,相同则弹出,popA的遍历序号加一

    • 题解:

      
      import java.util.ArrayList;
      import java.util.Stack;
      public class Solution {
          public boolean IsPopOrder(int [] pushA,int [] popA) {
              if (pushA.length == 0 || popA.length == 0 || popA.length != pushA.length)
                  return false;
              Stack<Integer> stack = new Stack<>();
              int j = 0;
              for (int i = 0; i < pushA.length; i++) {
                  stack.push(pushA[i]);
       
                  while (!stack.isEmpty() && stack.peek() == popA[j]){
                      stack.pop();
                      j++;
                  }
              }
              return stack.isEmpty();
          }
      }
      
  30. 从上到下打印二叉树(简化版的层次遍历)

    • 描述::从上往下打印出二叉树的每个节点,同层节点从左至右打印

    • 思路:二叉树的层次遍历算法中提到过,但是比这更复杂,需要判断哪一层,此题不用,直接用队列的数据结构,循环至队列空即可

    • 题解:

      import java.util.ArrayList;
      import java.util.LinkedList;
      import java.util.Queue;
      /**
      public class TreeNode {
          int val = 0;
          TreeNode left = null;
          TreeNode right = null;
      
          public TreeNode(int val) {
              this.val = val;
          }
      }
      */
      public class Solution {
          public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
              Queue<TreeNode> restemp=new LinkedList<TreeNode>();
              ArrayList<Integer> res=new ArrayList<>();
              if(root==null)
              {
                  return res;
              }
              restemp.offer(root);
              while(!restemp.isEmpty())
              {
                  TreeNode temp=restemp.poll();
                  res.add(temp.val);
                  if(temp.left!=null)
                  {
                      restemp.offer(temp.left);
                  }
                  if(temp.right!=null)
                  {
                      restemp.offer(temp.right);
                  }
              }
              return res;
          }
      }
      
  31. 二叉搜索树的后序遍历序列

    • 描述:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

    • 思路:对每一颗子树而言,其根节点都是后序遍历序列中的最后一个,循环的找到子树的根节点,判断其右子树的节点都比他大,左子树的节点都比他小,即可完成判断,当start>=end时,返回true

    • 题解:

      public class Solution {
          public boolean VerifySquenceOfBST(int [] sequence) {
              if(sequence==null)
              {
                  return false;
              }
              return isBST(sequence,0,sequence.length-1);
          }
          public boolean isBST(int [] seq,int start,int end){
              if(start>=end)
              {
                  return true;
              }
              int root=seq[end];
              int i=start;
              while(seq[i]<root)
              {
                  i++;
              }
              int split=i-1;
              for(int k=i;k<end;k++)
              {
                  if(seq[k]<root)
                  {
                      return false;
                  }
              }
              return isBST(seq,start,split)&&isBST(seq,split+1,end-1);
          }
      }
      

      问题:终结条件为什么是大于等于而不是等于呢?什么情况下会存在start<end的时候呢?

      就在子树可能是null的时候,划分边界时将start减一,此时如果不判断这种情况,将会造成栈的溢出,这种情况return true即可。

      注:[]是允许的,和null不一样在java中

  32. 二叉树的验证

    • 描述:给定一个二叉树,判断其是否是一个有效的二叉搜索树。

      假设一个二叉搜索树具有如下特征:

      • 节点的左子树只包含小于当前节点的数。
      • 节点的右子树只包含大于当前节点的数。
      • 所有左子树和右子树自身必须也是二叉搜索树。
    • 思路1:先返回中序序列,对中序序列进行检查即可

    • 题解1:

      /**
       * Definition for a binary tree node.
       * public class TreeNode {
       *     int val;
       *     TreeNode left;
       *     TreeNode right;
       *     TreeNode(int x) { val = x; }
       * }
       */
      class Solution {
          public boolean isValidBST(TreeNode root) {
              List<Integer> res=inorderTraversal(root);
              for(int i=0;i<res.size()-1;i++)
              {
                  if(res.get(i)>=res.get(i+1))
                  {
                      return false;
                  }
              }
              return true;
          }
          public List<Integer> inorderTraversal(TreeNode root) {
              List<Integer> res=new ArrayList<>();
              helper(root,res);
              return res;
          }
          public void helper(TreeNode root,List<Integer> res){
              if(root!=null)
              {
                  helper(root.left,res);
                  res.add(root.val);
                  helper(root.right,res);
              }
          }
      }
      
    • 思路2:递归(难点:判断的时候不能只判断左右子节点,还需要判断整棵左右子树),我们用一个布尔函数判断,他的传入值有树的根节点,上下界,对节点的右子树判断时,上界为无穷,下界为节点的值,左子树反之(卡边界值,左子树时上界是根节点的值,右子树时下界是根节点的值)

    • 题解2:

      /**
       * Definition for a binary tree node.
       * public class TreeNode {
       *     int val;
       *     TreeNode left;
       *     TreeNode right;
       *     TreeNode(int x) { val = x; }
       * }
       */
      class Solution {
          public boolean helper(TreeNode root,Integer lower,Integer higher){
              if(root==null) return true;
              if(lower!=null&&root.val<=lower) return false;
              if(higher!=null&&root.val>=higher) return false;
              if(!helper(root.left,lower,root.val)) return false;
              if(!helper(root.right,root.val,higher)) return false;
              return true;
          }
          public boolean isValidBST(TreeNode root) {
              return helper(root,null,null);
          }
      }
      
  33. 二叉搜索树的数组序列(困难)

    • 描述:从左向右遍历一个数组,通过不断将其中的元素插入树中可以逐步地生成一棵二叉搜索树。给定一个由不同节点组成的二叉树,输出所有可能生成此树的数组。

    • 错误思路:根节点是肯定比其左右子树的节点先插入,同一层的节点先后插入的结果是相同的,层次遍历的结果进行重新组合

    • 反省:实际上不是每一层进行排列组合,唯一确定的关系只有根节点与其子节点

    • 思路:当你插入一个节点的时候,你下一步可插入的节点放在一个List中,你需要对List进行for循环,每一步选择后,再递归的将当前节点的左右子节点加入List中,至List为空时完成了一次插入所有的节点,此时创建一个数组,加入res中,将上一步删除的节点重新加入队列中他原来的位置,就这样倒数着进行排列组合

    • 题解:

      /**
       * Definition for a binary tree node.
       * public class TreeNode {
       *     int val;
       *     TreeNode left;
       *     TreeNode right;
       *     TreeNode(int x) { val = x; }
       * }
       */
      class Solution {
          List<List<Integer>> res=new LinkedList<>();//结果数组
          public List<List<Integer>> BSTSequences(TreeNode root) {
              if(root==null)
              {
                  res.add(new LinkedList<>());
                  return res;
              }
              LinkedList<Integer> path=new LinkedList<>();//存放到目前为止的路径
              path.add(root.val);
              helper(root,new LinkedList<>(),path);
              return res;
          }
          public void helper(TreeNode root,LinkedList<TreeNode> queue,LinkedList<Integer> path){
              if(root==null) return;
              if(root.left!=null) queue.add(root.left);//root节点加进去后,其子节点和其兄弟节点都可能是下一个加入的对象
              if(root.right!=null) queue.add(root.right);
              if(queue.isEmpty())
              {
                  res.add(new LinkedList<>(path));
                  return;
              }
              int len=queue.size();
              for(int i=0;i<len;i++)
              {
                  TreeNode cur=queue.get(i);
                  queue.remove(i);
                  path.add(cur.val);
                  helper(cur,new LinkedList<>(queue),path);//注意此处传队列时传的是队列的复制,所以当返回到上一级的时候,queue并没有多出新的元素,每个节点的子节点还是由自己挑选
                  queue.add(i,cur);
                  path.removeLast();
              }
          }
      }
      
  34. 完全二叉树中的节点个数

    • 描述:给出一个完全二叉树,求出该树的节点个数

    • 思路:若用普通的遍历,可以求出结果,但是没有利用到完全二叉树的性质,故简便算法就此省略,我们递归的判断根节点的左右子树的深度,若相同,说明其左子树已经插满,其节点个数方便计算,再递归计算右子树即可,若不同,则右子树已满,递归计算左子树即可

    • 题解:

      /**
       * Definition for a binary tree node.
       * public class TreeNode {
       *     int val;
       *     TreeNode left;
       *     TreeNode right;
       *     TreeNode(int x) { val = x; }
       * }
       */
      class Solution {
          public int countNodes(TreeNode root) {
              if(root==null)
              {
                  return 0;
              }
              else
              {
                  int left=countLevels(root.left);
                  int right=countLevels(root.right);
                  if(left!=right)
                  {
                      return countNodes(root.left)+(1<<right);
                  }
                  else
                  {
                      return countNodes(root.right)+(1<<left);
                  }
              }
          }
          public int countLevels(TreeNode root){
              int i=0;
              if(root==null)
              {
                  return i;
              }
              else return max(countLevels(root.left),countLevels(root.right))+1;
          }
          public int max(int a,int b){
              if(a>=b)
              {
                  return a;
              }
              return b;
          }
      }
      
  35. 字符串转换整数(正则处理)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值