一刷 剑指offer 记录
目录
时间:2024/6/12
类型: 数据结构-链表
JZ6 从尾到头打印链表
描述:
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
如输入{1,2,3}的链表如下图:
示例图像
返回一个数组为[3,2,1]
0 <= 链表长度 <= 10000
代码:
方法一:递归
import java.util.*;
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
ArrayList<Integer> list=new ArrayList<>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
recursion(listNode);
return list;
}
public void recursion(ListNode listNode){
if(listNode==null){
return ;
}else{
recursion(listNode.next);
list.add(listNode.val);
}
}
}
方法二:栈
import java.util.*;
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list=new ArrayList<>();
Stack<Integer> stack =new Stack<>();
while(listNode!=null){
stack.push(listNode.val);
listNode=listNode.next;
}
while(!stack.isEmpty()){
list.add(stack.pop());
}
return list;
}
}
进度: 1/72
JZ24 反转链表
描述:
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
数据范围:
0≤𝑛≤1000
0≤n≤1000
要求:空间复杂度 𝑂(1) ,时间复杂度 𝑂(𝑛)。
如当输入链表{1,2,3}时,
经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
以上转换过程如下图所示:
代码:
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @return ListNode类
*/
public ListNode ReverseList (ListNode head) {
// write code here
ListNode pre=null;
ListNode cur=head;
ListNode temp=null;
while(cur!=null){
temp=cur.next;
cur.next=pre;
pre=cur;
cur=temp;
}
return pre;
}
}
进度: 2/72
JZ25 合并两个排序的链
描述:
输入两个递增的链表,单个链表的长度为,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围:0≤m≤1000,-1000≤节点值≤1000
要求:空间复杂度O(1),时间复杂度O(n)
如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},转换过程如下
图所示:
代码:
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead1 ListNode类
* @param pHead2 ListNode类
* @return ListNode类
*/
public ListNode Merge (ListNode pHead1, ListNode pHead2) {
// write code here
ListNode dummyHead=new ListNode(-1);
ListNode cur=dummyHead;
ListNode p1=pHead1;
ListNode p2=pHead2;
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;
}
if(p1!=null){
cur.next=p1;
}else{
cur.next=p2;
}
return dummyHead.next;
}
}
进度: 3/72
时间:2024/6/13
类型:数据结构-链表
JZ52 两个链表的第一个公共结点
描述:
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意
因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确
的)
数据范围:m≤1000
要求:空间复杂度O(1),时间复杂度O(n)
示例图像
代码:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode cur1=pHead1;
ListNode cur2=pHead2;
int len1=0;
int len2=0;
while(cur1!=null){
cur1=cur1.next;
len1++;
}
while(cur2!=null){
cur2=cur2.next;
len2++;
}
int gap=len1>=len2?len1-len2:len2-len1;
if(len1<len2){
ListNode temp=pHead1;
pHead1=pHead2;
pHead2=temp;
}
while(gap-->0){
pHead1=pHead1.next;
}
while(pHead1!=pHead2){
pHead1=pHead1.next;
pHead2=pHead2.next;
}
return pHead1;
}
}
进度: 4/72
JZ23 链表中环的入口结
描述:
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围:n≤10000,1<=结点值<=10000
要求:空间复杂度O(1),时间复杂度O(n)
代码:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
ListNode slow = pHead;
ListNode fast = pHead;
//如果不存在环就会跳出while循环
while (fast != null && fast.next != null) {
slow = slow.next;
fast =fast.next.next;
if (slow == fast) {
ListNode index = pHead;
while (index != slow) {
index = index.next;
slow = slow.next;
}
return index;
}
}
return null;
}
}
进度: 5/72
JZ22 链表中倒数最后k个结点
描述:
输入一个长度为n的链表,设链表中的元素的值为1,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为0的链表。
数据范围:0≤n≤105,0≤a,≤109,0≤k≤109
要求:空间复杂度O(n),时间复杂度O(n)
进阶:空间复杂度O(1),时间复杂度O(n)
代码:
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
public ListNode FindKthToTail (ListNode pHead, int k) {
// write code here
if(pHead==null) return null;
ListNode slow=pHead;
ListNode fast=pHead;
while(fast!=null&&k-->0){
fast=fast.next;
}
while(fast!=null){
fast=fast.next;
slow=slow.next;
}
//存在fast==null 但k>0 即链表长度< k
return k>0?null:slow;
}
}
进度: 6/72
时间:2024/6/14
类型:数据结构-链表
JZ35 复杂链表的复制
描述:
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针
random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中
请不要返回参数中的节点引用,否则判题程序会直接返回空)。下图是一个含有5个结点的复杂链
表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向nul的指针没有画
出。
代码:
import java.util.*;
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
public class Solution {
public RandomListNode Clone(RandomListNode pHead) {
if(pHead==null) return null;
// 存放 被拷贝的点 深拷贝后的点
Map<RandomListNode,RandomListNode> map=new HashMap<>();
RandomListNode newHead= new RandomListNode(pHead.label);
map.put(pHead,newHead);
RandomListNode cur=pHead;
RandomListNode newCur=newHead;
while(cur.next!=null){
RandomListNode newNode=new RandomListNode(cur.next.label);
map.put(cur.next,newNode);
newCur.next=newNode;
newCur=newCur.next;
cur=cur.next;
}
cur=pHead;
newCur=newHead;
while(cur!=null){
newCur.random=map.get(cur.random);
cur=cur.next;
newCur=newCur.next;
}
return newHead;
}
}
进度: 7/72
JZ76 删除链表中重复的结点
描述:
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复
的结点不保留,返回链表头指针。例如,链表1->2->3->3->4->4->5处
理后为1->2->5
数据范围:链表长度满足0≤n≤1000,链表中的值满足1≤val≤
1000
进阶:空间复杂度O(n),时间复杂度O(n)
例如输入{1,2,3,3,4,4,5}时,对应的输出为{1,2,5},对应的输入输出链表如下
图所示:
代码:
方法一:直接删除
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null || pHead.next == null) {
return pHead;
}
ListNode dummyHead = new ListNode(-1);
dummyHead.next = pHead;
ListNode cur = dummyHead;
//注意循环条件
while (cur.next != null && cur.next.next != null) {
//遇到相邻两个节点值相同
if (cur.next.val == cur.next.next.val) {
int temp = cur.next.val;
//将所有相同的都跳过
while (cur.next != null && cur.next.val == temp) {
cur.next = cur.next.next;
}
} else {
cur = cur.next;
}
}
return dummyHead.next;
}
}
方法二:Hash表
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null) {
return pHead;
}
//用Hash表统计每个节点出现的次数
Map<Integer, Integer> map = new HashMap<>();
ListNode cur = pHead;
while (cur!= null) {
map.put(cur.val, map.getOrDefault(cur.val, 0) + 1);
//不要漏了
cur=cur.next;
}
ListNode dummyHead = new ListNode(-1);
dummyHead.next = pHead;
cur = dummyHead;
while (cur.next != null) {
//如果为1则不是重复节点
if (map.get(cur.next.val) != 1) {
//跳过重复节点
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return dummyHead.next;
}
}
进度: 8/72
JZ18 删除链表的节点
描述:
<给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节
点。返回删除后的链表的头节点。
1.此题对比原题有改动
2.题目保证链表中节点的值互不相同
3.该题只会输出返回的链表和结果做对比,所以若使用C或C++语言,你不
需要free或delete被删除的节点
数据范围:
0<=链表节点值<=10000
0<=链表长度<=10000 /em>
代码:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null) {
return pHead;
}
//用Hash表统计每个节点出现的次数
Map<Integer, Integer> map = new HashMap<>();
ListNode cur = pHead;
while (cur!= null) {
map.put(cur.val, map.getOrDefault(cur.val, 0) + 1);
//不要漏了
cur=cur.next;
}
ListNode dummyHead = new ListNode(-1);
dummyHead.next = pHead;
cur = dummyHead;
while (cur.next != null) {
//如果为1则不是重复节点
if (map.get(cur.next.val) != 1) {
//跳过重复节点
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return dummyHead.next;
}
}
进度: 9/7
时间:2024/6/15
类型:数据结构-二叉树
JZ55 二叉树的深度
描述:
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的条路径,最长路径
的长度为树的深度,根节点的深度视为1。
数据范围:节点的数量满足0≤n≤100,节点上的值满足0≤wal≤100
进阶:空间复杂度O(1),时间复杂度O(几)
代码:
方法一:递归
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @param val int整型
* @return ListNode类
*/
public ListNode deleteNode (ListNode head, int val) {
if(head==null) return null;
ListNode dummyHead=new ListNode(-1);
dummyHead.next=head;
ListNode pre=dummyHead;
ListNode cur=dummyHead.next;
while(cur.val!=val){
cur=cur.next;
pre=pre.next;
}
pre.next=cur.next;
return dummyHead.next;
}
}
方法二:层次遍历
import java.util.*;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public int TreeDepth(TreeNode root) {
if(root==null) return 0;
int depth=0;
Deque<TreeNode> que=new LinkedList<TreeNode>();
que.offer(root);
while(!que.isEmpty()){
int size=que.size();
depth++;
while(size-->0){
TreeNode node=que.poll();
if(node.left!=null){
que.offer(node.left);
}
if(node.right!=null){
que.offer(node.right);
}
}
}
return depth;
}
}
进度: 10/72
JZ54 二叉搜索树的第k个节点
描述:
给定一棵结点数为n二叉搜索树,请找出其中的第k小的TreeNode?结点值。
1.返回第k小的节点值即可
2.不能查找的情况,如二叉树为空,则返回-1,或者k大于等等,也返回-1
3.保证n个节点的值不一样
数据范围:0≤n≤1000,0≤k≤1000,树上每个结点的值满足0≤val≤1000
进阶:空间复杂度O(n),时间复杂度O(n)
代码:
方法一:递归
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param proot TreeNode类
* @param k int整型
* @return int整型
*/
int num=0;//记录遍历到第几个数
int ans = -1;
//二叉搜索树 中序遍历
public int KthNode (TreeNode proot, int k) {
if (proot == null) return -1;
traversal(proot, k);
return ans;
}
public void traversal(TreeNode cur, int k) {
if (cur == null) return;
traversal(cur.left, k);
num++;
if (num == k) {
ans = cur.val;
}
traversal(cur.right, k);
}
}
方法二:栈
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param proot TreeNode类
* @param k int整型
* @return int整型
*/
public int KthNode(TreeNode proot, int k) {
if (proot == null) return -1;
Stack<TreeNode> stack = new Stack<>();
TreeNode node = proot;
//两个条件缺一不可
while (node != null || !stack.isEmpty()) {
if (node != null) {
stack.push(node);
node = node.left;
} else {
node = stack.pop();
k--;
if (k == 0) {
return node.val;
}
//不需要判断 node.right是否为空
node = node.right;
}
}
return -1;
}
}
进度: 11/72
JZ7 重建二叉树
描述:
给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
代码:
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param preOrder int整型一维数组
* @param vinOrder int整型一维数组
* @return TreeNode类
*/
public TreeNode reConstructBinaryTree (int[] preOrder, int[] vinOrder) {
// write code here
return buildTree(preOrder,vinOrder,0,preOrder.length,0,vinOrder.length);
}
//左闭右开
public TreeNode buildTree(int[] preOrder, int[] vinOrder,int preLeft,int preRight,int vinLeft,int vinRight){
if(preLeft>=preRight) return null;
TreeNode cur=new TreeNode(preOrder[preLeft]);
int vinIndex=0;
for(vinIndex=vinLeft;vinIndex<vinRight;vinIndex++){
if(vinOrder[vinIndex]==cur.val) break;
}
int leftLen=vinIndex-vinLeft;
cur.left=buildTree(preOrder,vinOrder,preLeft+1,preLeft+1+leftLen,vinLeft,vinIndex);
cur.right=buildTree(preOrder,vinOrder,preLeft+1+leftLen,preRight,vinIndex+1,vinRight);
return cur;
}
}
进度: 12/72
时间:2024/6/16
类型:数据结构-二叉树
JZ26 树的子结构
描述:
输入两棵二叉树A,B,判断B是不是A的子结构。(我们约定空树不是任意一个树的
子结构)
代码:
import java.util.*;
/**
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;
}
return traversalroot1(root1,root2);
}
//遍历root1的每个节点
public boolean traversalroot1(TreeNode root1,TreeNode root2){
if(root1==null) return false;
boolean ans=traversal(root1,root2);
if(ans) return true;
boolean left=traversalroot1(root1.left,root2);
boolean right=traversalroot1(root1.right,root2);
return left||right;
}
//判断是否为子结构
public boolean traversal(TreeNode cur,TreeNode root2){
// if(cur==null&&root2==null) return true;
// if(cur!=null&&root2==null) return true;
if(root2==null) return true;
if(cur==null&&root2!=null) return false;
if(cur.val!=root2.val) return false;
boolean left=traversal(cur.left,root2.left);
boolean right=traversal(cur.right,root2.right);
return left&&right;
}
}
进度: 13/72
JZ27 二叉树的镜像
描述:
操作给定的二叉树,将其变换为源二叉树的镜像。
数据范围:二叉树的节点数0≤n≤1000,二叉树每个节点的值0≤wal≤
1000
要求:空间复杂度O()。本题也有原地操作,即空间复杂度O(1)的解法,时间
复杂度O(n)
代码:
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
if(pRoot==null) return null;
traversal(pRoot);
return pRoot;
}
//可以用前序或者是后序遍历,但不能用中序
public TreeNode traversal(TreeNode cur){
if(cur==null) return null;
//重点
swap(cur);
traversal(cur.left);
traversal(cur.right);
return cur;
}
public void swap(TreeNode root){
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
}
}
进度: 14/72
JZ32 从上往下打印二叉树
描述:
不分行从上往下打印出二叉树的每个节点,同层节点从左至右打印。例如输入
{8,6,10,#,#,2,1,如以下图中的示例二叉树,则依次打印8,6,10,2,1(空节点不打印,
跳过),请你将打印的结果存放到一个数组里面,返回。
代码:
import java.util.*;
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 {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> list=new ArrayList<>();
if(root==null) return list;
Deque<TreeNode> que=new LinkedList<>();
TreeNode node=root;
que.offer(node);
while(!que.isEmpty()){
int size=que.size();
while(size-->0){
node=que.poll();
list.add(node.val);
if(node.left!=null){
que.offer(node.left);
}
if(node.right!=null){
que.offer(node.right);
}
}
}
return list;
}
}
进度: 15/72
时间:2024/6/17
类型:数据结构-二叉树
JZ33 二叉搜索树的后序遍历序列
描述:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则
返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。
数据范围:节点数量0≤m≤1000,节点上的值满足1≤wal≤105,保证
节点上的值各不相同
要求:空间复杂度O(m),时间时间复杂度O(n)
提示:
1.二叉搜索树是指父亲节点大于左子树中的全部节点,但是小于右子树中的全部节点
的树。
代码:
import java.util.*;
public class Solution {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence==null||sequence.length==0){
return false;
}
return f(sequence,0,sequence.length-1);
}
//思路:递归
//首先从左往右找到第一个大于root值的节点
//这时从这个节点往后到root节点到前一个节点,
//如果发现有大于root节点的值,则不是正确的二叉树后序遍历序列
public boolean f(int[] sequence,int i,int j){
//如果之前一直没找到错误,返回true
if(i>=j) return true;
int root=sequence[j];
int p=i;
while(sequence[p]<root&&p<j) p++;
for(int a=p+1;a<j;a++){
if(sequence[a]<root){
return false;
}
}
return f(sequence,i,p-1)&&f(sequence,p,j-1);
}
}