目录
二维数组中的查找
替换空格
从尾到头打印链表
重建二叉树
二叉树的下一个节点
用两个栈实现队列
二叉树的下一个节点
算法与数据操作
一、递归和循环
斐波那契数列
跳台阶
变态跳台阶
矩形覆盖
二、查找和排序
二进制中1的个数
数值的整数次方
代码的规范性
代码的完整性
调整数组顺序使奇数位于偶数前面
链表中倒数第k个结点
反转链表
复杂链表的复制
二叉搜索树与双向链表
数组中只出现一次的数字
数组中的逆序对
对称的二叉树
把二叉树打印成多行
序列化二叉树
题目
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路
从右上角(或者左下角)开始
大于待查找值——从右向左剔除所在列
小于待查找值——从上向下剔除所在行
代码
——JAVA
public class Solution {
public boolean Find(int target, int [][] array) {
if(array.length==0||array[0].length==0)
//注意理解————在二维数组中
//数组名.length指示数组的行数;数组名[行下标] .length指示该行中的元素个数。
return false;
int col=array[0].length-1;//右上角的列数
int row=0;//右上角的行数
int temp=array[row][col];
while(target!=temp){
if(col>0 && rowtemp){
row=row+1;//排除上面的行
}
else if(target
题目
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
注意:空格(1个字符)→“%20”(3个字符)
应用
用于网络编程中,将URL参数中的特殊符号转换为服务器可以识别的字符
e.g. 空格(ASCⅡ码为32)十六进制0x20→%20 #(ASCⅡ码为35)十六进制0x20→%23)
思路
C++中,假定考虑在原来的字符串上进行替换,保证输入的字符串后面有足够的空余内存
如果从头到尾进行扫描替换,复杂度为O(n2);而从尾到头复制替换,所有字符只复制一次,复杂度降为O(n)
先遍历得到空格数目以及需要增加的内存,再从后向前,设置两个指针,分别指向原始的字符串末尾和替换后的字符末尾
JAVA中,下面给出的解法是直接创建新的字符串空间(不够好,最好考虑在原始字符串上进行修改)
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的
StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
StringBuffer 类支持的主要方法:
序号 | 方法描述 |
---|---|
1 | public StringBuffer append(String s) 将指定的字符串追加到此字符序列。 |
2 | public StringBuffer reverse() 将此字符序列用其反转形式取代。 |
3 | public delete(int start, int end) 移除此序列的子字符串中的字符。 |
4 | public insert(int offset, int i) 将 int 参数的字符串表示形式插入此序列中。 |
5 | replace(int start, int end, String str) 使用给定 String 中的字符替换此序列的子字符串中的字符。 |
问题
对c++中基于原始字符串修改的方法不够理解
1.新建字符串
2.在原有字符串上更改,需要重新设置长度setLength()
str.toString用法理解
代码1
public class Solution {
public String replaceSpace(StringBuffer str) {
StringBuffer res=new StringBuffer();//创建新空间
int len=str.length()-1;//字符串编号
//从后往前 遍历寻找空格并替换
for(int i=len;i>=0;i--){
if(str.charAt(i)==' '){
res.append("02%");//替换
}
else
res.append(str.charAt(i));
}
return res.reverse().toString();//反序得到修改后字符串
}
}
代码2
public class Solution {//在原有字符串上修改
public String replaceSpace(StringBuffer str) {
int length=str.length();
int num_Space=0;
for(int i=0;i=0;index--){//这里也可以for(;index>=0;index--)
if(str.charAt(index)==' '){
str.setCharAt(newindex--,'0');
str.setCharAt(newindex--,'2');
str.setCharAt(newindex--,'%');
}
else{
str.setCharAt(newindex--,str.charAt(index));
}
}
return str.toString();
}
}
测试用例
特殊输入测试(字符串是nullptr指针;字符串是一个空字符串;只有一个空格字符;有连续多个空格)
题目
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
思路
1.栈——后进先出,具有一定鲁棒性
2.递归——若链表太长,则可能导致函数调用栈溢出
代码
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
import java.util.Stack;//注意不要忘记
public class Solution {
public ArrayList printListFromTailToHead(ListNode listNode) {
Stack stack=new Stack();
while(listNode!=null){
stack.push(listNode.val);//先将原始链表头存入栈
listNode=listNode.next;
}
ArrayList list=new ArrayList();
while(!stack.isEmpty()){
list.add(stack.pop());//从栈中输出,保证后进先出
}
return list;//要有个ArrayList作为返回值
}
}
题目
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路
前序遍历——根左右
中序遍历——左根右
后序遍历——左右根
可以得到根节点,再根据递归思想分别得到左右子树的具体节点
递归思想:每次将左右子树当成新的子树进行处理,中序的左右子树索引很好找,前序的开始结束索引通过计算中序中左右子树的大小来计算,然后递归求解,直到startPreorder>endPreorder || startInoreder>endInorder说明子树整理完毕
方法每次返回一个根节点
问题
不确定public,static什么时候用;对递归的理解不够透彻
代码
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode reConstructBinaryTree(int [] preorder,int [] inorder) {
if(preorder==null||inorder==null||preorder.length<1||inorder.length!=preorder.length)
{return null;}
TreeNode root= ConstructBinaryTree(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
return root;
}
/**构建二叉树
* preorder前序遍历序列:ps开始位置,pe结束位置
* inorder中序遍历序列:is开始位置,ie结束位置
*/
private TreeNode ConstructBinaryTree(int [] preorder,int ps,int pe,int [] inorder,int is, int ie){
//前序遍历的开始位置大于结束位置时,说明有问题
if(ps>pe||is>ie){
return null;
}
TreeNode root=new TreeNode(preorder[ps]);
for(int i=is;i<=ie;i++){
if(preorder[ps]==inorder[i]){
root.left=ConstructBinaryTree(preorder,ps+1,ps+(i-is),inorder,is,i-1);//前序:根左右;中序:左根右
root.right=ConstructBinaryTree(preorder,(i-is)+ps+1,pe,inorder,i+1,ie);
}
}
return root;
}
}
题目
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路
中序遍历:左 -> 根 -> 右
分三种情况:
代码
/*
public class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null;
TreeLinkNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode)
{
//节点为空,返回空
if(pNode==null){
return null;
}
//当前节点有右子树,返回右子树的最左子树
if(pNode.right!=null){
TreeLinkNode node=pNode.right;
while(node.left!=null){
node=node.left;
}
return node;
}
//当前节点无右子树
//判断当前节点若非其父节点的左子树,将其父节点赋给当前节点,继续判断,直至满足,返回其父节点
else{
while(pNode.next!=null){
TreeLinkNode root=pNode.next;
if(pNode==root.left){return root;}
else{pNode=root;}
}
return null;
}
}
}
题目
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
思路
注意要把stack1里pop出来的元素再push回去,不然在以下用例会出错
push1,push2,push3,pop,pop,push4,pop,push5,pop
代码
import java.util.Stack;
public class Solution {
Stack stack1 = new Stack();
Stack stack2 = new Stack();
//(先进先出)队列的push,pop函数
public void push(int node) {
stack1.push(node);//
}
public int pop() {
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
int popnode=stack2.pop();
while(!stack2.isEmpty()){
stack1.push(stack2.pop());
}//int queue=stack2.pop();
return popnode;
}
}
题目
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路
中序遍历:左 -> 根 -> 右
递归:需要在内存栈中分配空间 ,容易引发调用栈溢出
(从0开始,第0项为0)
思路:f(0)=0,f(1)=1,f(2)=1,f(n)=f(n-1)+f(n-2)
注意:1.递归,思路效率很低(o(n^2))
2.从下往上计算o(n)
public class Solution {
public int Fibonacci(int n) {
return getFibonacci(n);
}
private int getFibonacci(int n){
if(n==0||n==1){
return n;
}
int n1=0;
int n2=1;
int value=0;
for(int num=0;num<=n-2;num++)
{
value=n2+n1;
n1=n2;
n2=value;
}
return value;
}
}
3.不够实用:矩阵[1 1;1 0]的n-1次方——用递归实现(n/2的平方再将其平方即得到n次方)即时间复杂度o(logn)
思路:
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
(先后次序不同算不同的结果)
即斐波那契数列
思路:找规律
f(n) = 2 * f(n-1)
同上
思路:斐波那契
思路:
1.Java自带函数:Integer.bitCount() 熟悉API使用
2.如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
public class Solution {
public int NumberOf1(int n) {
int count = 0;
while(n!= 0){
count++;
n = n & (n - 1);
}
return count;
}
}
题目:
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
思路:
n
为非负数时,>> 1
和/ 2
的结果是一样的n
为负数且还是偶数时,>> 1
和/ 2
的结果是一样的n
为负数且还是奇数时,>> 1
和/ 2
的结果是不一样的-5 / 2 = -2
,5 / 2 = 2
。这表明 除二是向零取整-5 >> 1 = -3
,5 >> 1 = 2
。这表明 右移一位是向下取整
方法:
1.符合逻辑思考:循环或者递归(如下)
//循环
public double Power(double base, int n) {
boolean Normal=true;
if(base==0){
if(n==0){Normal=false;}
return 0;
}
if(n==0){
return 1;
}
if(n==1){
return base;
}
int abs=n;
if(n<0){
abs=-n;
}
double result=1;
for(;abs>0;abs--){
result*=base;
}
if(n<0){
result=1/result;
}
return result;
}
}
//递归
public class Solution {
public double Power(double base, int exponent) {
boolean Normal=true;//记录是否正常返回值
//0的0次方无意义,非0次方为0
if(base==0){
if(exponent<=0){
Normal=false;
}
return 0;
}
//非零数字的0次方
if(exponent==0){
return 1;
}
//非零数字的一次方
if(exponent==1){
return base;
}
//
int absexponent=exponent;
if(exponent<0){
absexponent=-exponent;
}
double result=Power(base,absexponent>>1);//右移1位代替除以2
result*=result;
if((exponent&1)==1){
result=result*base;
}
if(exponent<0){
result=1/result;
}
return result;
}
}
2.
public double Power(double base, int n) {
double res = 1,curr = base;
int exponent;
if(n>0){
exponent = n;
}else if(n<0){
if(base==0)
throw new RuntimeException("分母不能为0");
exponent = -n;
}else{// n==0
return 1;// 0的0次方
}
while(exponent!=0){
if((exponent&1)==1)
res*=curr;
curr*=curr;// 翻倍
exponent>>=1;// 右移一位
}
return n>=0?res:(1/res);
}
1.功能测试:如果是int、longlong都无法表示的大数,可能需要用特殊的数据结构来表示
2.边界测试
3.负面测试
错误处理方法:
1.返回值——与系统API一致,但不能方便使用计算结果
2.全局变量——能够方便使用计算结果,但是用户可能会忘记检查全局变量
3.抛出异常——可以为异常分类,逻辑清晰,但是有些语言不支持,且对性能有影响
题目:
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
代码:
public void reOrderArray(int [] array) {
if(array.length==0||array.length==1||array==null){
return;
}
List list_odd=new ArrayList<>();
List list_even=new ArrayList<>();//只记录奇数偶数的index
for(int i:array){
if(i%2==1){
list_odd.add(i);
}
else{
list_even.add(i);
}
}
list_odd.addAll(list_even);
for(int j=0;j
题目:输入一个链表,输出该链表中倒数第k个结点。
代码:(采用两个指针,第一个指向原链表的第k个位置,第二个指向原链表的第1个位置;再同时后移直到第一个指向链表最后一个位置,此时第二个指针指向结点则为我们需要的)
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(head==null||k<=0){
return null;
}
ListNode node1=head;//用于做参考,先指向链表第k个位置,最后到最后一个位置
ListNode node2=head;//用于获取,从head第1个位置到倒数第k个位置
for(int i=1;i
题目:输入一个链表,反转链表后,输出新链表的表头。
代码:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
import java.util.Stack;
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head==null){
return null;
}
Stack stack=new Stack<>();
ListNode index=head;
stack.push(head.val);
while(index.next!=null){
stack.push(index.next.val);
index=index.next;
}
ListNode rehead=head;
while(head.next!=null){
head.val=stack.pop();
head=head.next;
}
head.val=stack.pop();
return rehead;
}
}
参考代码:
public class Solution {
public static ListNode ReverseList(ListNode head) {
if(head==null)
return null;
ListNode reversedHead=null;
ListNode current=head;
ListNode tmp=null;
ListNode pre=null;
while(current!=null){
tmp=current.next;
current.next=pre;
if(tmp==null)
reversedHead=current;
pre=current;
current=tmp;
}
return reversedHead;
}
}
总是不对,真的不知道为啥
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
分析:排序的双向列表,即新建一个树结构,其中节点为现有树结构经过中序遍历后的顺序,这些节点的left,right不变即可
二叉搜索树(Binary Search Tree)————或为空,或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树。
(即中序遍历为递增的)
思路:
1.遍历(常常会超出运算空间或时间)
2.非遍历
3.Morris遍历
public class Solution {
public TreeNode Convert(TreeNode pRootOfTree) {
TreeNode p = pRootOfTree, pre = null, res = null;
while (p != null) {
while (p.left != null) { //当前节点左子树非空时
TreeNode q = p.left; //设置q,为当前节点的左子树根节点
while (q.right != null) { //循环:只要q右子树非空 就令q为其右子树
q = q.right;
}
q.right = p; // 使用为空的右子树记录当前节点
TreeNode tmp = p.left;
p.left = null;
p = tmp; //使当前节点更新为当前节点的左子树根节点,直至为null退出
}
p.left = pre;
//判断当前节点是否需要被加入双向链表中
if (pre == null) {
res = p;//中序遍历数列的第一项,即最小一个左节点
}
else {
pre.right = p;
}
pre = p;//用pre记录下当前节点
p = p.right;//更新当前节点为当前节点的右子树根节点
}
return res;//返回中序遍历数列
}
}
1.hashMap
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
HashMap map=new HashMap();
for(int i=0;i
2.异或
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] a,int num1[] , int num2[]) {
if(a.length==0)
return;
//全体异或
int num=0;
for(int i=0;i>1;
index++;
}
//分组异或
for(int i=0;i>index;
if((tmp&1)==1){
num1[0]^=a[i];
}else{
num2[0]^=a[i];
}
}
}
}
首先分成两个数组,分别递归归并排序,结果存到辅助数组
逆序对个数 = 数组彼此间逆序对的个数 + 子组内的逆序对个数
组内逆序对如何确定——建立辅助数组用于存储较大的值
分别从大到小对比两个数组中每个元素的大小(这里需要考虑是 奇数 的情况)
public class Solution {
public int InversePairs(int [] array) {
int len = array.length;
if(array== null || len <= 0){
return 0;
}
return mergeSort(array, 0, len-1);
}
public int mergeSort(int [] array, int start, int end){
if(start == end)
return 0;
int mid = (start + end) / 2;
int left_count = mergeSort(array, start, mid);//对两个子数组分别归并排序
int right_count = mergeSort(array, mid + 1, end);
int i = mid, j = end;
int [] copy = new int[end - start + 1];
int copy_index = end - start;//辅助数组
int count = 0;
while(i >= start && j >= mid + 1){
//保证i从mid开始减小直到start,j从end开始减小直到mind+1
if(array[i] > array[j]){//////////////////////若第一组值大于第二组
copy[copy_index--] = array[i--];//将较大值存入辅助数组
count += j - mid;//逆序对个数为第二组个数
if(count > 1000000007){
count %= 1000000007;//防止数据量过大
}
}else{//////////////////////若第一组值小于第二组
copy[copy_index--] = array[j--];//仍然将较大值存入辅助数组
}
}
while(i >= start){//第二组提前处理完了,说明第一组取值都较小,按顺序存进去即可
copy[copy_index--] = array[i--];
}
while(j >= mid + 1){//第一组提前处理完了
copy[copy_index--] = array[j--];
}
i = 0;
while(start <= end) {
array[start++] = copy[i++];
}
return (left_count+right_count+count)%1000000007;//数组内部逆序对+左右子组逆序对
}
}
1.递归
2.非递归(引入栈的数据结构,push存储左右结点,pop出来进行判断,并重新存入下一层四个左右结点)
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.Stack;
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
//1.递归方法
// if(pRoot==null)
// return true;
// return isSym(pRoot.left,pRoot.right);
// }
// public boolean isSym(TreeNode left,TreeNode right){
// if(left==null && right==null)
// return true;
// if(left==null || right==null)
// return false;
// if(left.val==right.val){
// return isSym(left.left,right.right) &&
// isSym(left.right,right.left);//关键语句
// }
// return false;
//2.非递归方法
if(pRoot==null)
return true;
Stack s=new Stack();
s.push(pRoot.left);
s.push(pRoot.right);
while(!s.isEmpty()){
TreeNode right=s.pop();
TreeNode left=s.pop();
if(right==null&&left==null)
continue;//当前循环结束 且进入下一次循环
if(right==null || left==null)
return false;
if(right.val!=left.val)
return false;
s.push(left.left);
s.push(right.right);
s.push(left.right);
s.push(right.left);
}
return true;
}
}
import java.util.ArrayList;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList > Print(TreeNode pRoot) {
ArrayList> list=new ArrayList>();
LayerNode(pRoot,1,list);//第一层
return list;
}
public void LayerNode(TreeNode p,int depth,ArrayList> list){
if(p==null)
return;
if(depth>list.size())//当层数大于当前list内存储层数时,新建一个用于存储新层
list.add(new ArrayList());
list.get(depth-1).add(p.val);//关键步骤:加入当前根节点的值
LayerNode(p.left,depth+1,list);
LayerNode(p.right,depth+1,list);
}
}
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
String Serialize(TreeNode root) {
if(root==null){
return"#,";
}
StringBuffer res=new StringBuffer(root.val+",");
res.append(Serialize(root.left));
res.append(Serialize(root.right));
return res.toString();
}
TreeNode Deserialize(String str) {
String[] res=str.split(",");
Queue queue=new LinkedList();
for(int i=0;i queue){
String val=queue.poll();//取出并删除
if(val.equals("#")){//基本类型的比较用==,String类型用equals()方法
return null;
}
TreeNode node=new TreeNode(Integer.parseInt(val));//Integer.parseInt()返回int型整数
node.left = preOrder(queue);
node.right = preOrder(queue);
return node;
}
}
首先判断有没有环(通过快慢指针是否相等)
接着判断入口位置(相遇位置与链表头 和入口的距离相等)
注意!判断边界情况
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead.next==null || pHead.next.next==null)
return null;
ListNode slow=pHead.next;
ListNode fast=pHead.next.next;
while(fast!=null){
if(slow==fast){
fast=pHead;
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return slow;
}
slow=slow.next;
fast=fast.next.next;
}
return null;
}
}
Python2
思路:建立临时链表存储遍历该链表,然后出现重复则为入口
class Solution:
def EntryNodeOfLoop(self, pHead):
tempList = []
p = pHead
while p:
if p in tempList:
return p
else:
tempList.append(p)
p = p.next