剑指offer(简单到复杂顺序题解)
58 .左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"
三种解法:
1.字符串切片
class Solution {
public String reverseLeftWords(String s, int n) {
return s.substring(n,s.length()) + s.substring(0,n);
}
}
2.列表遍历拼接
class Solution {
public String reverseLeftWords(String s, int n) {
StringBuilder res=new StringBuilder();
for(int i=n;i<s.length();i++){
res.append(s.charAt(i));
}
for(int i=0;i<n;i++){
res.append(s.charAt(i));
}
return res.toString();
}
}
3.字符串遍历拼接
class Solution {
public String reverseLeftWords(String s, int n) {
String res="";
for(int i=n;i<s.length();i++){
res=res + s.charAt(i);
}
for(int i=0;i<n;i++){
res=res+s.charAt(i);
}
return res;
}
}
27.二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/
2 7
/ \ /
1 3 6 9
镜像输出:
4
/
7 2
/ \ /
9 6 3 1
两种解法:
1.递归
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root==null)return null;
TreeNode tmp = root.left;
root.left=root.right;
root.right=tmp;
mirrorTree(root.left);
mirrorTree(root.right);
return root;
}
}
2.辅助栈或队列
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null) return null;
Stack<TreeNode> stack = new Stack<>() {{ add(root); }};
while(!stack.isEmpty()) {
TreeNode node = stack.pop();
if(node.left != null) stack.add(node.left);
if(node.right != null) stack.add(node.right);
TreeNode tmp = node.left;
node.left = node.right;
node.right = tmp;
}
return root;
}
}
55.二叉树的深度
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
在总结这题的方法之前,先回顾一下二叉树的几种遍历方法
1.前序遍历
访问顺序:根节点、前序遍历左子树、前序遍历右子树
class Solution {
List<Integer> list = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
if(root == null) return list;
list.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
return list;
}
}
2.中序遍历
访问顺序:中序遍历左子树、根节点、中序遍历右子树
class Solution {
List<Integer> list=new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root == null)return list;
inorderTraversal(root.left);
list.add(root.val);
inorderTraversal(root.right);
return list;
}
}
3.后序遍历
访问顺序:后序遍历左子树、后序遍历右子树、根节点
class Solution {
List<Integer> list = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
if(root == null)return list;
postorderTraversal(root.left);
postorderTraversal(root.right);
list.add(root.val);
return list;
}
}
4.层序遍历(重要)
访问顺序:从上到下、从左到右依次访问每一个节点
class Solution {
List<List<Integer>> resList = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null) return resList;
Queue<TreeNode> queue = new LinkedList<>();
int levelSize = 1;
queue.offer(root);
List<Integer> list = new ArrayList<>(); ;
while(!queue.isEmpty()){
TreeNode node = queue.poll();
list.add(node.val);
levelSize--;
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
if(levelSize == 0){
resList.add(list);
levelSize = queue.size();
list = new ArrayList<>();
}
}
return resList;
}
}
根据以上几种遍历方式,求二叉树的深度
递归法
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
22.链表倒数第K个节点(双指针)
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode former =head;
ListNode latter = head;
for(int i=0;i<k;i++){
former=former.next;
}
while(former!=null){
former=former.next;
latter=latter.next;
}
return latter;
}
}
17.打印从1到最大的n位数
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999
(因为该题要求返回的是int类型 所以不考虑大数越界问题,范围就是int32的范围)
class Solution {
public int[] printNumbers(int n) {
int max=(int)Math.pow(10,n)-1;
int[] res = new int [max];
for(int i=0;i<res.length;i++){
res[i]=i+1;
}
return res;
}
}
考虑大数的方法
class Solution {
int[] res;
int nine = 0, count = 0, start, n;
char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
public int[] printNumbers(int n) {
this.n = n;
res = new int[(int)Math.pow(10, n) - 1];
num = new char[n];
start = n - 1;
dfs(0);
return res;
}
void dfs(int x) {
if(x == n) {
String s = String.valueOf(num).substring(start);
if(!s.equals("0")) res[count++] = Integer.parseInt(s);
if(n - start == nine) start--;
return;
}
for(char i : loop) {
if(i == '9') nine++;
num[x] = i;
dfs(x + 1);
}
nine--;
}
}
05.替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"
class Solution {
public String replaceSpace(String s) {
int n=s.length();
char[] array = new char[n * 3];
int size = 0;
for(int i=0;i<n;i++){
char c=s.charAt(i);
if(c==' '){
array[size++]='%';
array[size++]='2';
array[size++]='0';
}else{
array[size++]=c;
}
}
String str=new String(array,0,size);
return str;
}
}
class Solution {
public String replaceSpace(String s) {
StringBuilder res = new StringBuilder();
for(Character c : s.toCharArray())
{
if(c == ' ') res.append("%20");
else res.append(c);
}
return res.toString();
}
}
06.从尾到头打印链表
入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)
Java 算法流程:
递推阶段: 每次传入 head.next ,以 head == null(即走过链表尾部节点)为递归终止条件,此时直接返回。
回溯阶段: 层层回溯时,将当前节点值加入列表,即tmp.add(head.val)。
最终,将列表 tmp 转化为数组 res ,并返回即可。
class Solution {
ArrayList<Integer> tmp = new ArrayList<Integer>();
public int[] reversePrint(ListNode head) {
recur(head);
int[] res = new int[tmp.size()];
for(int i = 0; i < res.length; i++)
res[i] = tmp.get(i);
return res;
}
void recur(ListNode head) {
if(head == null) return;
recur(head.next);
tmp.add(head.val);
}
}
24.反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode pre = null;
while(cur!=null){
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}
54.二叉搜索树第K大的节点
利用中序遍历的降序遍历,提前跳出编写代码
class Solution {
int res,k;
public int kthLargest(TreeNode root, int k) {
this.k=k;
dfs(root);
return res;
}
void dfs(TreeNode root){
if(root == null) return;
dfs(root.right);
if(k == 0) return;
if(--k == 0) res = root.val;
dfs(root.left);
}
}
25.合并两个排序的链表(伪头结点)
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dum =new ListNode(0);
ListNode cur =dum;
while(l1!=null && l2!=null){
if(l1.val < l2.val){
cur.next=l1;
l1 = l1.next;
}else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next=l1!=null ? l1 : l2;
return dum.next;
}
}