offer 03 数组中的重复数字
题目描述:
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
我的思路:
一.用字典。
存在的问题:①一开始字典并未赋值,所以不能用if(dic[nums[i]]!=null),此时会报错说key值不存在,只有用ADD方法添加了key和value后才可以判断。
修改:可以使用dic.ContainsKay();方法来判断Key是否存在!
代码:
public class Solution {
public int FindRepeatNumber(int[] nums) {
Dictionary<int,int> dic=new Dictionary<int,int>();
for(int i=0;i<nums.Length;i++)
{
if(dic.ContainsKey(nums[i])) return nums[i];
else { dic.Add(nums[i],i); }
}
return -1;
}
}
执行用时:176 ms
内存消耗:38.9 MB
很慢!
思路二:用一个长度为n的数组,value[nums[i]]=nums[i];如果插入时发现已有值,则返回。
针对数组初始化是都为0的情况,设置一个int值存储0出现的次数。
代码:
public class Solution {
public int FindRepeatNumber(int[] nums) {
int [] value=new int [nums.Length];
int numberofzero=0;
for(int i=0;i<nums.Length;i++)
{
if(nums[i]==0) numberofzero++;
if(numberofzero>1) return 0;
if((nums[i]!=0)&&value[nums[i]]==nums[i]) return nums[i];
else value[nums[i]]=nums[i];
}
return -1;
}
}
执行用时:
172 ms, 在所有 C# 提交中击败了44.69%的用户
内存消耗:
36 MB, 在所有 C# 提交中击败了100.00%的用户
思路三:对于思路二的改进,用value来存储次数即可,就可以避免了0的特殊情况
BUG:对于重复的判断语句出现问题,应该是》=1,而不是》=2
方法改进:用一个int类型来存储当前查询的序号,会快上一点。int curr=nums【i】;
代码:
public class Solution {
public int FindRepeatNumber(int[] nums) {
int [] value=new int [nums.Length];
for(int i=0;i<nums.Length;i++)
{
if(value[nums[i]]>=1) return nums[i];
else value[nums[i]]++;
}
return -1;
}
}
执行用时:
172 ms, 在所有 C# 提交中击败了44.69%的用户
内存消耗:
36 MB, 在所有 C# 提交中击败了100.00%的用户
解题方法一:原地置换
思路:将整个数组从0开始排序,如果有重复数字,则返回
难点:①如何通过一次循环完成排序?②重复数字的判断条件
代码:
public class Solution {
public int FindRepeatNumber(int[] nums) {
int temp;
for(int i=0;i<nums.Length;i++)
{
while(nums[i]!=i)//如果用了if,不能通过一次循环来置换
{
if(nums[i]==nums[nums[i]]) return nums[i];//通过判断数字是否相同来判断是否重复!
temp=nums[i];
nums[i]=nums[temp];
nums[temp]=temp;
}
}
return -1;
}
}
执行用时:
160 ms, 在所有 C# 提交中击败了79.38%的用户
内存消耗:
35.3 MB, 在所有 C# 提交中击败了100.00%的用户
offer 04 二维数组的查找
题目描述:
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
我的思路:
首先要缩小范围,先求得target可能存在的最大行号,再求得最小行号。再求最小列,最后再求最大列。
注意先判断两种特殊情况:target小于最小值和大于最大值
代码:
public class Solution {
public bool FindNumberIn2DArray(int[][] matrix, int target) {
if(matrix == null || matrix.Length == 0 || matrix[0].Length == 0) return false;
int row=matrix.GetLength(0);
int col=matrix[0].Length;
if(matrix[0][0]>target||matrix[row-1][col-1]<target) return false;//判断特殊情况
int minrow,maxrow,mincol,maxcol;
int left=0,right=row-1,mid;
while(left<=right)//求最大行
{
mid=(left+right)/2;
if(matrix[mid][0]==target) return true;
else if(matrix[mid][0]<target) left=mid+1;
else right=mid-1;
}
maxrow=right; left=0;right=maxrow;
while(left<right)//求最小行
{
mid=(left+right)/2;
if(matrix[mid][col-1]==target) return true;
else if(matrix[mid][col-1]<target) left=mid+1;
else right=mid;
}
minrow=right; left=0;right=col-1;
while(left<right)//求最小列
{
mid=(left+right)/2;
if(matrix[maxrow][mid]==target) return true;
else if(matrix[maxrow][mid]<target) left=mid+1;
else right=mid;
}
mincol=right;left=mincol;right=col-1;
while(left<=right)//求最大列
{
mid=(left+right)/2;
if(matrix[0][mid]==target) return true;
else if(matrix[0][mid]<target) left=mid+1;
else right=mid-1;
}
maxcol=right;
for(int i=minrow;i<=maxrow;i++)//查找
for(int j=mincol;j<=maxcol;j++)
if(matrix[i][j]==target) return true;
return false;
}
}
执行用时:
148 ms, 在所有 C# 提交中击败了60.39%的用户
内存消耗:
31.2 MB, 在所有 C# 提交中击败了100.00%的用户
offer 05替换空格
题目描述:
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1:
输入:s = “We are happy.”
输出:“We%20are%20happy.”
我的思路:将字符串逐个扫描,遇到空格则用%20替代
代码:
public class Solution {
public string ReplaceSpace(string s) {
string result="";
for(int i=0;i<s.Length;i++)
{
if(s[i]==' ') result+="%20";
else result+=s[i];
}
return result;
}
}
执行用时:
124 ms, 在所有 C# 提交中击败了9.77%的用户
内存消耗:
25 MB, 在所有 C# 提交中击败了100.00%的用户
offer 06 从尾到头打印链表
题目描述:
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
我的解题思路: 用栈
代码:
public class Solution {
public int[] ReversePrint(ListNode head) {
Stack<int> stack=new Stack<int>();
while(head!=null)
{
stack.Push(head.val);
head=head.next;
}
int [] num=new int[stack.Count]; int i=0;
while(stack.Count!=0)
num[i++]=stack.Pop();
return num;
}
}
执行用时:
276 ms, 在所有 C# 提交中击败了89.35%的用户
内存消耗:
31.6 MB, 在所有 C# 提交中击败了100.00%的用户
offer 07 重建二叉树
题目描述:
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/
9 20
/
15 7
我的解题思路:
首先,把根节点,以及根结点的DLR,LDR数组传进去。找到根节点的左孩子和右孩子,并且赋予给根结点,然后不断对左孩子和右孩子进行递归。
代码:
public class Solution {
public TreeNode BuildTree(int[] preorder, int[] inorder) {
if(preorder.Length==0||inorder.Length==0) return null;
TreeNode root =new TreeNode(preorder[0]);
Process(root,0,preorder.Length-1,0,inorder.Length-1,preorder,inorder);//首先要把root传进去
return root;
}
void Process(TreeNode root,int preleft,int preright,int inleft,int inright,int [] preorder,int[] inorder)
{
if(root.val==int.MaxValue||preleft==preright||inleft==inright) return ;
//判断不需要处理的情况
int index=0,LDlength,RDlength; //index来保存root的序号。
TreeNode LD=new TreeNode(int.MaxValue); TreeNode RD=new TreeNode(int.MaxValue);
//左孩子和右孩子
for(int i=inleft;i<=inright;i++)
if(inorder[i]==root.val) index=i;
LDlength=index-inleft; if(LDlength>0) LD=new TreeNode(preorder[preleft+1]);
//找到左孩子
RDlength=inright-index; if(RDlength>0) RD=newTreeNode(preorder[preleft+LDlength+1]);
//找到右孩子
if(LD.val!=int.MaxValue) root.left=LD; if(RD.val!=int.MaxValue) root.right=RD;
//通过判定是否存在左右孩子
//把左孩子/右孩子当作一颗新树,做递归操作。
Process(LD,1+preleft,preleft+LDlength,inleft,index-1,preorder,inorder);
Process(RD,preleft+LDlength+1,preright,index+1,inright,preorder,inorder);
}
}
执行用时:
132 ms, 在所有 C# 提交中击败了62.50%的用户
内存消耗:
27.1 MB, 在所有 C# 提交中击败了100.00%的用户
offer 9 用栈实现队列(未完成
offer 10 斐波那契数列(未完成
题目描述:
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
我的思路:
由规律寻找可知,当N大于4时,有数列:1,2,3,5,8,13.。。数列的个数等于N-2.最后的值等于 数列的最后两个之和。
代码:
public class Solution {
public int Fib(int n) {
if(n==0)return 0;
if(n<=2) return 1;
if(n==3) return 2;
int []nums=new int[n-2];
nums[0]=1;nums[1]=2;
for(int i=0;i<n-4;i++)
nums[i+2]=nums[i]+nums[i+1];
int result=nums[nums.Length-1]+nums[nums.Length-2];
return result%1000000007;
}
}
出现的问题:相加之和大于了int,出现了负数!
offer 10-2 青蛙跳台阶(未完成
题目描述:
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
示例 2:
输入:n = 7
输出:21
提示:
0 <= n <= 100
我的思路:
首先先确定有几种可能性。可能性等于N/2。为甚是这个数,这个数代表着多少个2可以跳完,也意味着最多的2,如果不够,则剩下只能是1,所以方案的总数。接下来,求解媒重方案的可能情况即可,排列组合,C(2+1)的总数/2的总数。
代码:
public class Solution {
public int NumWays(int n) {
int result=0;
for(int i=n/2;i>=0;i--)
result+=Arrange(i,n-2*i);
return result;
}
int Arrange(int x,int y)
{
if(x<y) return Arrange(y,x);
int nums=1,result=1;
for(int i=x+y;i>x;i--)
nums*=i;
for(int i=y;i>0;i--)
result*=i;
return nums/result;
}
}
出现的问题:当台阶《=25时正确,大于25时错误!
offer 11旋转数组的最小值
题目描述:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
我的思路:从后往前找,找到前面一个比现在大的时候,意味着现在就是要求的值。
问题:没有考虑没有旋转的情况,应该在代码的末尾返回开头(如果查找过程没有返回,则意味着没有旋转)
代码:
public class Solution {
public int MinArray(int[] numbers) {
for(int i=numbers.Length-1;i>0;i--)
{
if(numbers[i-1]>numbers[i]) return numbers[i]; //查找
}
return numbers[0];//返回开头
}
}
执行用时:
100 ms, 在所有 C# 提交中击败了98.36%的用户
内存消耗:
25.2 MB, 在所有 C# 提交中击败了100.00%的用户
offer12 矩阵中的路径
题目描述:
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
示例 1:
输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:
输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false
提示:
1 <= board.length <= 200
1 <= board[i].length <= 200
我的思路:
第一个版本:一开始只考虑了开头可能时重复的情况,没有考虑到中间部分也可以重复
遇到的问题,双重循环时的break只会跳出内重循环,最后for的i和j都会大于条件!
字典采用<int[],bool>泛型,存储的是索引,每次添加采用了new关键字,所以尽管值是相同的,在字典里面仍然被识别为不同的。
第二个版本:不采用if else,而采用if,深度搜索的思想。要注意currnt的部分
代码:
public class Solution {
char[][] board;//矩阵
int row;//行
int col;//列
int length;//路径长度
string word;//字符串
int current=0;//已经找到的个数
Dictionary<int, bool> dt;//字典,存储不能使用的行号和列号
public bool Exist(char[][] board, string word) {
length = word.Length;//赋初始值
this.board = board;
this.word = word;
row = board.GetLength(0);
col = board[0].Length;
if(row*col<length) return false;
bool result=false;
for (int i=0; i < row; i++)//寻找开头
for (int j=0; j < col; j++)
{
if (board[i][j] == word[0])
{
if (word.Length == 1) return true;
else
{
dt = new Dictionary<int, bool>();//每次重新初始化
result = Arrange(i, j);
if (result == true) return true;
}
}
}
return result;
}
bool Arrange(int i, int j)
{
current++;
if (current == length) return true;
char target = word[current];
dt.Add(i * col + j, true);//添加已经找到的
if (i - 1 >= 0 && board[i - 1][j] == target && !dt.ContainsKey((i - 1) * col + j))
{
if (Arrange(i - 1, j)) return true;
}
if (i + 1 < row && board[i + 1][j] == target && !dt.ContainsKey((i + 1) * col + j))
{
if (Arrange(i + 1, j)) return true;
}
if (j - 1 >= 0 && board[i][j - 1] == target && !dt.ContainsKey(i * col + j - 1))
{
if (Arrange(i, j - 1)) return true;
}
if (j + 1 < col && board[i][j + 1] == target && !dt.ContainsKey(i * col + j + 1))
{
if (Arrange(i, j + 1)) return true;
}
dt.Remove(i * col + j);//删去已经找到的
current--;//当前不是需要的,要减去!
return false;
}
}
执行用时:
168 ms, 在所有 C# 提交中击败了31.03%的用户
内存消耗:
29.1 MB, 在所有 C# 提交中击败了100.00%的用户
offer 13 机器人的运动范围
题目描述:
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
示例 1:
输入:m = 2, n = 3, k = 1
输出:3
示例 2:
输入:m = 3, n = 1, k = 0
输出:1
提示:
1 <= n,m <= 100
0 <= k <= 20
我的思路:首先画了个矩阵观察了一下,发现列超过十行的时候会发生新的变化,又通过别人的代码受到提醒,要采用分治算法,也就是把矩阵切割开。首先是列的切割,如果第十列可以到达,则切割之后的,并进入函数,否则只处理前十列。对于每一行,先判断首尾就可以算出每一行可以走多少格。
代码:
public class Solution {
int[] row;int[] col;int rowlength;int collength; int k;//矩阵和K
public int MovingCount(int m, int n, int k) {
row=new int[m];col=new int[n];
rowlength=m;collength=n;this.k=k;
for(int i=0;i<rowlength;i++)//行数组初始化
{
if(i<100)
row[i]=(i/10)+(i%10);
else row[i]=1;
}
for(int i=0;i<collength;i++)//列数组初始化
{
if(i<100)
col[i]=(i/10)+(i%10);
else col[i]=1;
}
int result=0;result=GetCount(0,n-1);//传入函数,0是列的开头,n-1是列的结尾
return result;
}
int GetCount(int x,int y)//对于列进行切分,x是列的开头,y是列的结尾
{
int nums=0;
if((y-x+1)>10)//超过十行
{
if(row[0]+col[x+9]<=k)
nums+=GetCount(x+10,y);//可到达才处理
y=x+9;//超过十行就要对y进行切割
}
//计算每一行的个数
for(int i=0;i<rowlength;i++)
{
if(row[i]+col[y]<=k) nums+=y-x+1;
else if(row[i]+col[x]>k) break;
else
{
nums+=k-(row[i]+col[x])+1;
}
}
return nums;
}
}
执行用时:
48 ms, 在所有 C# 提交中击败了72.17%的用户
内存消耗:
14.5 MB, 在所有 C# 提交中击败了100.00%的用户
offer 14 剪绳子
题目描述:
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:
2 <= n <= 58
我的思路:可以通过规律猜想出,绳子最长就要由3 和2组成。首先考虑特殊情况,比如说2,3,4,之后就根据n%3之后的余数进行判断。如果是0,所以被3整除,此时可以直接相乘返回;如果是1,则需要划出一个3组成4,这个时候才是最大的;如果是2,则可以直接相乘。
代码:
public class Solution {
public int CuttingRope(int n) {
if(n==2) return 1;
if(n==3) return 2;
if(n==4) return 4;
int numsof3=0,numsof2=0;//3的个数和2的个数
numsof3=n/3;numsof2=n%3;
if(numsof2==0) return (int)Math.Pow(3,numsof3);
else if(numsof2==2) return (int)Math.Pow(3,numsof3)*2;//2的个数只要一个即可
else return (int)Math.Pow(3,numsof3-1)*4; //要凑成4
}
}
执行用时:
44 ms, 在所有 C# 提交中击败了81.82%的用户
内存消耗:
14.9 MB, 在所有 C# 提交中击败了100.00%的用户
offer 15 二进制中1的个数
题目描述:
请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
我的思路:根据二进制的定义,计算出正数%2有多少个1即可。当正数=0时结束循环
代码:
public class Solution {
public int HammingWeight(uint n) {
int result=0;
while(n>0)
{
if(n%2==1) result++;
n=n/2;
}
return result;
}
}
执行用时:
48 ms, 在所有 C# 提交中击败了66.38%的用户
内存消耗:
14.7 MB, 在所有 C# 提交中击败了100.00%的用户
offer 16(未完成)
offer 17 打印从1到最大的n位数
题目描述:
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
示例 1:
输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]
说明:
用返回一个整数列表来代替打印
n 为正整数
我的思路:
只要计算出最大的n位数即可。
代码:
public class Solution {
public int[] PrintNumbers(int n) {
int max=0;
for(int i=0;i<n;i++)
max+=9*(int)Math.Pow(10,i);//计算最大的n位数
int [] result=new int[max];
for(int i=0;i<max;i++)
result[i]=i+1;
return result;
}
}
执行用时:
268 ms, 在所有 C# 提交中击败了93.94%的用户
内存消耗:
31.3 MB, 在所有 C# 提交中击败了100.00%的用户
offer 18删除链表的节点
题目描述:给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
示例 1:
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
我的思路:没啥好想的,找到之后删除就可以了
代码:
public class Solution {
public ListNode DeleteNode(ListNode head, int val) {
if(head.val==val) return head.next;
ListNode p=head;
while(p.next!=null)
{
if(p.next.val==val) {p.next=p.next.next;break;}
else p=p.next;
}
return head;
}
}
执行用时:
112 ms, 在所有 C# 提交中击败了64.63%的用户
内存消耗:
25.4 MB, 在所有 C# 提交中击败了100.00%的用户
offer 19匹配正则表达式(困难
offer 20 表示数值的字符串(重新编写
题目描述:
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、“±5”、"-1E-16"及"12e+5.4"都不是。
注意:此题的重点在于构造规则,只要规则构造的足够完善就可以了。这道题目我错了十九次,①空格错误②e错误③。④+,-错误。
这道题目我的解法相当简单但是恶心,需要重新编写代码!
代码:
public class Solution {
int before=0;
int length=0;
string s;
public bool IsNumber(string s) {
s=s.TrimStart(); s = s.TrimEnd(' ');
length=s.Length;
if(length==0||s==" ") return false;
s.TrimStart();
s.TrimEnd(' ');
this.s=s;
int i=0;
while(i<s.Length)
{
if(i!=0&&(s[i]=='+'||s[i]=='-')) return false;
if(Char.IsLetter(s[i]))
{
if((i==0)||(s[i]!='e'&&s[i]!='E')) return false;
}
if(s[i]=='e'||s[i]=='E')
{
if(i==length-1) return false;//后面没有字符
if(!Char.IsDigit(s[i-1])&&s[i-1]!='.') return false;//前面的情况
if(i==length-2&&(s[i+1]=='+'||s[i+1]=='-')) return false;//+的情况
if(s[i+1]=='+'||s[i+1]=='-'){i+=2;return ProcessE(i);}//+,-的情况
return ProcessE(++i);
}
if(s[i]=='.') {
if( (i-1<0||!Char.IsDigit(s[i-1])) && (i+1>=length || (s[i+1]!='e'&&!Char.IsDigit(s[i+1]) )) ) return false;
else{
if((i-1<0||!Char.IsDigit(s[i-1]))&& (s[i+1]=='e') ) return false;
else return ProcessNet(++i);
}
}
if(s[i]==' ') return false;
if(Char.IsDigit(s[i])) before++;
i++;
}
return true;
}
bool ProcessNet(int i)
{
if(i==length) return true;
if(!Char.IsDigit(s[i])&&s[i]!='e') return false;
if(s[i]=='e')
{
if(i==length-1) return false;
if(i==length-2&&(s[i+1]=='+'||s[i+1]=='-')) return false;
if(s[i+1]=='+'||s[i+1]=='-') return ProcessE(i+2);//+,-的情况
else return ProcessE(++i);
}
else {before++;i++;}
return ProcessNet(i);
}
bool ProcessE(int i)//适用于满足e的条件的情况下处理e后面的字符
{
if(i==length) return true;
if(!Char.IsDigit(s[i])) return false;
else {before++;i++;}
return ProcessE(i);
}
}
执行用时:
92 ms, 在所有 C# 提交中击败了76.67%的用户
内存消耗:
25.6 MB, 在所有 C# 提交中击败了100.00%的用户
offer 21调整数组顺序使奇数在偶数之前
题目:
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
示例:
输入:nums = [1,2,3,4]
输出:[1,3,2,4]
注:[3,1,2,4] 也是正确的答案之一。
我的思路:双指针解法
BUG:会多做一次交换,使得边界的两个数字交换。原因:在于语句的顺序不同
public class Solution {
int [] nums;
public int[] Exchange(int[] nums) {
this.nums=nums;
int left=0,right=nums.Length-1;
while(left<right)
{
if(nums[left]%2==0&&nums[right]%2==1)//注意要先判断,这里就是出BUG的地方
{
Swap(left,right);
}
if(nums[left]%2==1) left++;
if(nums[right]%2==0) right--;
}
return nums;
}
void Swap(int i,int j)
{
int x=nums[j];
nums[j]=nums[i];
nums[i]=x;
}
}
执行用时:
328 ms, 在所有 C# 提交中击败了70.45%的用户
内存消耗:
37.1 MB, 在所有 C# 提交中击败了100.00%的用户
offer 22 链表中倒数第K个节点
题目:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
我的思路:双指针解法!其实也是等距解法
代码:
public class Solution {
public ListNode GetKthFromEnd(ListNode head, int k) {
ListNode left=head;
ListNode right=head;
for(int i=0;i<k;i++)
if(right!=null) right=right.next;
while(right!=null)
{
left=left.next;
right=right.next;
}
return left;
}
}
执行用时:
112 ms, 在所有 C# 提交中击败了51.56%的用户
内存消耗:
24.8 MB, 在所有 C# 提交中击败了100.00%的用户
offer28 对称的二叉树
题目描述:
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/
2 2
/ \ /
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/
2 2
\
3 3
我的思路:
根的左节点按照DLR顺序输出字符串
根的有节点按照DRL顺序输出字符串
比对两个字符串是否相等来判断
代码:
ublic class Solution {
public bool IsSymmetric(TreeNode root) {
if(root==null) return true;
if(root.left==root.right&&root.left==null) return true;
if(root.left==null&&root.right!=null) return false;
if(root.right==null&&root.left!=null) return false;
if(root.left.val!=root.right.val) return false;
string result1=Printroot(root.left);
string result2=Printright(root.right);
return String.Equals(result1,result2);
}
string Printroot(TreeNode root)
{
if(root==null) return "null";
string result="";
result+=root.val.ToString();
result+=Printroot(root.left);
result+=Printroot(root.right);
return result;
}
string Printright(TreeNode root)
{
if (root == null) return "null";
string result = "";
result += root.val.ToString();
result += Printright(root.right);
result += Printright(root.left);
return result;
}
}
太慢了!
执行用时:
136 ms, 在所有 C# 提交中击败了8.00%的用户
内存消耗:
26.2 MB, 在所有 C# 提交中击败了7.69%的用户
Offer 29. 顺时针打印矩阵
描述:
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
我的思路:
一开始,我以为是按照右下左上的顺序来遍历,最外层是一个while循环,跳出条件是数组满了,里面是一个if和三个else,分别是右下左上,当右边没有遍历过时优先考虑右边。但是这个方法有一个弊端,就是走到了上的时候,因为右是没有遍历的,所以就会断开上的循环,优先右的循环。
错误示例:
public int[] SpiralOrder(int[][] matrix) {
if(matrix==null) return null;
int i=0;int row=0,col=0;
int index=matrix[0][0]-1;
int rowlength=matrix.GetLength(0);
int collength=matrix[0].Length;
int length=rowlength*collength;
int[] result=new int [length];
while(i<length)
{
if(row<rowlength&&collength<collength&&matrix[row][col]!=index)
{
result[i++]=matrix[row][col];
matrix[row][col++]=index;
}
else if(row<rowlength&&collength>0&&matrix[row][col]!=index)
{
result[i++]=matrix[row][col];
matrix[row][col--]=index;
}
else if(row>0&&collength>0&&matrix[row][col]!=index)
}
}
}
另一个思路:不妨把这个看做是状态机,按照右下左上的状态循环,达到状态切换条件是就切换状态,可以用一个队列来存储状态。(注:用链表是最好的,够快;出队条件要在状态机方法里面写)
遇到的问题:判断是否转变状态的条件:①当前是否无法进行②当前方向是否已经遍历过
在判断转变状态的时候,要进行值的转变:比如说行+1,就是往下。但是这时候有个问题,就是当前的值又会重复判断,导致转变状态的时候无法进入下一状态,所以要把当前状态也剔除。
注意观察返回值。空值处理: 当 matrix 为空时,直接返回空列表 [] 即可。
代码:
public int[] SpiralOrder(int[][] matrix) {
if(matrix==null) return new int [0];
if(matrix.GetLength(0)==0) return new int [0];
int i = 0; int row = 0, col = 0;
int index =int.MinValue; //这个是这个代码的BUG,这个代码选择了一个数字作为
int rowlength = matrix.GetLength(0);//标志位,就会使得遇到这个数字的时候就会报错
int collength = matrix[0].Length;
int length = rowlength * collength;
int[] result = new int[length];
Queue<int> queue = new Queue<int>();
for (int j = 1; j <= 4; j++)
queue.Enqueue(j);
while (i < length)
{
int k = queue.Peek();
switch (k)
{
case 1: //注意此处对于col和row的赋值。
if (col >= collength|| (matrix[row][col] == index)) { queue.Enqueue(queue.Dequeue()); col--; row++; }
else
{
result[i++] = matrix[row][col];
matrix[row][col++] = index;
}
break;
case 2:
if (row >= rowlength||matrix[row][col]==index) { queue.Enqueue(queue.Dequeue()); row--;col--; }
else
{
result[i++] = matrix[row][col];
matrix[row++][col] = index;
}
break;
case 3:
if (col < 0|| matrix[row][col] == index) { queue.Enqueue(queue.Dequeue()); col++; row--; }
else
{
result[i++] = matrix[row][col];
matrix[row][col--] = index;
}
break;
case 4:
if (row < 0|| matrix[row][col] == index) { queue.Enqueue(queue.Dequeue()); row++;col++; }
else
{
result[i++] = matrix[row][col];
matrix[row--][col] = index;
}
break;
}
}
return result;
}
执行用时:
360 ms, 在所有 C# 提交中击败了6.06%的用户
内存消耗:
33.5 MB, 在所有 C# 提交中击败了17.71%的用户
其他解题思路:
关键点在于边界的判断,我对于边界的判断过于繁琐了,我可以设置一个上下左右的边界,每次到达了边界就让边界减一就好了
剑指Offer 32
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回:
[3,9,20,15,7]
我的思路:
用一个队列去存储结点,每次出队把结点填入数组,同时把出队的结点的左右结点填入队列。
代码:
public class Solution {
public int[] LevelOrder(TreeNode root) {
if(root==null) return new int[0];
List<int> _list=new List<int>();
Queue<TreeNode> _queue=new Queue<TreeNode>();
_queue.Enqueue(root);
while(_queue.Count>0)
{
if(_queue.Peek().left!=null)
_queue.Enqueue(_queue.Peek().left);
if(_queue.Peek().right!=null)
_queue.Enqueue(_queue.Peek().right);
_list.Add(_queue.Dequeue().val);
}
return _list.ToArray();
}
}
时间:50% 内存:24%
剑指Offer 39
题目描述:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
参考思想:
因为这个元素是占一半以上的,所以如果用人数优势去挤兑的话,最后存活的一定是占多数的元素。
比如说一个拳击台,如果拳击台上没人,则选手登台。如果拳击台上有人,如果是队友,则一起登台,如果不是,则双方一起下台,留到最后台上的必定是多数元素。
代码:
public class Solution {
public int MajorityElement(int[] nums) {
int current=0; //目前的选手
int note=0; //台上的人数
for(int i=0;i<nums.Length;i++)
{
if(note==0)
{
current=nums[i];
note++;
continue;
}
if(nums[i]==current)
note++;
else note--;
}
return current;
}
}
剑指Offer 40
题目描述:
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
我的思路:用冒泡排序,只排前k个数即可。
代码:
public class Solution {
public int[] GetLeastNumbers(int[] arr, int k) {
if(k==0) return new int[0];
int[] result=new int[k];
int current;
for(int i=0;i<k;i++)
{
current=i;
for(int j=i;j<arr.Length;j++)
if(arr[j]<arr[current])
current=j;
result[i]=arr[current];
arr[current]=arr[i];
}
return result;
}
}
解题思路一:
用快排,快排的思想是小于枢纽的在左边,大于枢纽的在右边,所以只要找到这个枢纽的位置为K,则左边的值就是要求的。
剑指Offer 52 两个链表的第一个公共节点
题目描述:输入两个链表,找出它们的第一个公共节点。
参考解题思路:
本质上还是说长的链表走C+X步能到公共节点,短的链表走C步能到公共节点。所以如果存在公共节点,则在C处,双方一起走,如果相遇了,如果是null,则表示没有,否则就是公共节点。
假如是由公共的部分,那么我们在长的链表中可以够造出跟短链表一样的长度,经过相同的步,就一定能遇到相同的部分,否则,如果都是null,则表示没有公共的部分。
解法一:双指针法
如果链表一个长一个短,那么从头开始遍历,则长的部分会慢于短的部分,无法确保双方在同一起跑点,所以要削弱短的优势,则让短的跑完短的时候,回到长的去跑,这样,双方的起跑点便一致了。其实就是让短的找到长的当做长度等于短的起始部分
代码:
public class Solution {
public ListNode GetIntersectionNode(ListNode headA, ListNode headB) {
ListNode left,right;
left=headA;right=headB;
while(left!=right)
{
left=left!=null? left.next:headB;
right=right!=null? right.next:headA;
}
return left;
}
}
解法二:首先得知长的长度,短的长度,然后构建出短的长度
public class Solution {
public ListNode GetIntersectionNode(ListNode headA, ListNode headB) {
ListNode left,right;
left=headA;right=headB;
int x=0,y=0;
while(left!=null)
{
x++;
left=left.next;
}
while(right!=null)
{
y++;
right=right.next;
}
if(x>y) return Process(headA,headB,x-y);
else return Process(headB,headA,y-x);
}
public ListNode Process(ListNode a,ListNode b,int count)
{
ListNode left=a;ListNode right=b;
while(count>0)
{
count--;
left=left.next;
}
while(left!=right)
{
left=left.next;
right=right.next;
}
return left;
}
}
剑指Offer 53 I. 在排序数组中查找数字 I
题目描述:
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
我的思路:利用二分查找算法,若找到target,则往前和往后找。
注意判断条件:Left>Right则表示没有
写算法的时候既要从有的思路去想,又要考虑到没有的时候要怎么判断!。
代码:
public class Solution {
int[] nums;
int count=0;
public int Search(int[] nums, int target) {
this.nums=nums;
return Process(0,nums.Length-1,target);
}
public int Process(int left,int right,int target)
{
if(left>right) return 0;
int index=(left+right)/2;
if(nums[index]>target)
return Process(left,right-1,target);
else if(nums[index]<target)
return Process(left+1,right,target);
count++;
int x,y;x=index-1;y=index+1;
while(x>=0&&nums[x]==target)
{
count++;
x--;
}
while(y<=right&&nums[y]==target)
{
count++;
y++;
}
return count;
}
}
执行用时:
116 ms, 在所有 C# 提交中击败了80.00%的用户
内存消耗:
26.9 MB, 在所有 C# 提交中击败了6.67%的用户
剑指Offer 54 二叉搜索树的第k大节点
题目描述:
给定一棵二叉搜索树,请找出其中第k大的节点。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/
1 4
2
输出: 4
我的思路:因为是二叉搜索树,所以按照LDR的顺序输出树,然后找到Count-K即可。
注意LDR,LRD,DLR的递归顺序。
代码:
public class Solution {
List<int> nums=new List<int>();
public int KthLargest(TreeNode root, int k) {
Process(root);
return nums[nums.Count-k];
}
void Process(TreeNode root)
{
if(root==null)
return;
Process(root.left);
nums.Add(root.val);
Process(root.right);
}
}
执行用时:
120 ms, 在所有 C# 提交中击败了75.89%的用户
内存消耗:
28.4 MB, 在所有 C# 提交中击败了16.07%的用户
剑指Offer 55 I. 二叉树的深度
题目描述:
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
我的思路:
使用队列去输出二叉树,但是遇到的问题是如何判断哪些节点是同一层的,卡在了这里。看了答案之后发现可以用一个辅助队列,把同一层的放在同一个队列中,只有上一层的队列全部输出之后,再把存储的全部层插入到队列中。
代码:
public class Solution {
public int MaxDepth(TreeNode root) {
if(root==null) return 0;
int count=1;
Queue<TreeNode> stack=new Queue<TreeNode>();
Queue<TreeNode> temp=new Queue<TreeNode>();
stack.Enqueue(root);
while(stack.Count!=0)
{
TreeNode n=stack.Dequeue();
if(n.left!=null)
temp.Enqueue(n.left);
if(n.right!=null)
temp.Enqueue(n.right);
if(stack.Count==0)
{ if(temp.Count!=0) //这个一定要有,因为是判断是否有下一层,有才+1。
{
count++;
stack=temp;
temp=new Queue<TreeNode>(); //注意,这里才初始化,因为交换的时候才清空
}
}
}
return count;
}
}
执行用时:
116 ms, 在所有 C# 提交中击败了47.10%的用户
内存消耗:
25.6 MB, 在所有 C# 提交中击败了26.45%的用户