字符串
**剑指 Offer 05. 替换空格 第一道题3.19**
请实现一个函数,把字符串 s
中的每个空格替换成"%20"。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RZHNHsZ6-1626916827939)(C:\Users\12479\AppData\Roaming\Typora\typora-user-images\image-20210319215145157.png)]
并且’-'负号为45
示例 1:
输入:s = "We are happy."
输出:"We%20are%20happy."
//方法一:利用了Array数组
class Solution{
public String replaceString(String s ){
char[] array = new char[s.length() * 3 ];
int size = 0;
for(int i= 0;i <s.length();i++){
char c = s.charAt(i );
if (' '==c ){
array[size++] = '%';
array[size++] = '2';
array[size++] = '0';
}else{
array[size++] = c;
}
}
//一个数组转换成字符串;
String nes = new String(array,0 ,size);
return nes;
}
}
//方法二.这个方法速度快一点,利用StringBulider
class Slotion2{
public String repaceSpace(String s){
StringBuilder sb= new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (' ' == s.charAt(i)){
sb.append("%20");
}else {
sb.append(s.charAt(i ));
}
}
return sb.toString();
}
}
剑指 Offer 67. 把字符串转换成整数
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: "42"
输出: 42
示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:
输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。
代码
剑指 Offer 19. 正则表达式匹配
请实现一个函数用来匹配包含'. '
和'*'
的正则表达式。模式中的字符'.'
表示任意一个字符,而'*'
表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"
与模式"a.a"
和"ab*ac*a"
匹配,但与"aa.a"
和"ab*a"
均不匹配。
方法一:
利用java 的函数
class Solution{
public boolean isMatch(String s, String p) {
return s.matches(p);
}
}
方法二:动态规划
class Solution {
public boolean isMatch(String s, String p) {
int m = s.length();
int n = p.length();
boolean[][] f = new boolean[m + 1][n + 1];
f[0][0] = true;
for (int i = 0; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (p.charAt(j - 1) == '*') {
f[i][j] = f[i][j - 2];
if (matches(s, p, i, j - 1)) {
f[i][j] = f[i][j] || f[i - 1][j];
}
} else {
if (matches(s, p, i, j)) {
f[i][j] = f[i - 1][j - 1];
}
}
}
}
return f[m][n];
}
public boolean matches(String s, String p, int i, int j) {
if (i == 0) {
return false;
}
if (p.charAt(j - 1) == '.') {
return true;
}
return s.charAt(i - 1) == p.charAt(j - 1);
}
}
链表
剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
**注意:**此题对比原题有改动
方法 1:
没有用到带头结点的
class Solution {
public ListNode deleteNode(ListNode head, int val) {
ListNode pDeleteFront = head;
if(head.val ==val){
head = head.next;
}else{
while(pDeleteFront.next.val !=val&&pDeleteFront.next!=null){
pDeleteFront = pDeleteFront.next;
}
if(pDeleteFront.next.val==val){
ListNode deleltedNode;
deleltedNode = pDeleteFront.next;
pDeleteFront.next = deleltedNode.next;
}
}
return head;
}
}
方法2:
下面这个方法,仍然需要遍历整个链表,找到这值的节点,
时间复杂度为o(n)平均,空间为O(1);
这里给的是val一个节点的值,而不是一个节点的;如果给节点,可以用节点后面的一个节点复制给当前节点,然后删除当前节点,就能实现O(1)的复杂度了;
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if(head.val ==val){
return head.next;
}
ListNode creedentnode =head.next;
ListNode prenode =head;
while(creedentnode.next!=null &&creedentnode.val!=val){
prenode = creedentnode;
creedentnode = creedentnode.next;
}
prenode.next =creedentnode.next;
return head;
}
}
剑指 Offer 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6
个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6
。这个链表的倒数第 3
个节点是值为 4
的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
if(head ==null || k <1){
return null;
}
ListNode fast=head;
//相当于for(i=1;i<k;i++).块指针fast走了k-1步.
while(k-->1){
if(fast.next ==null){
return null;
}else{
fast = fast.next;
}
}
ListNode slow = head;
//块指针走到尾节点停下.不是走到了null节点才停下的
while(fast.next !=null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
剑指 Offer 06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
用栈,
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
Deque<Integer> stack = new LinkedList<Integer>();
ListNode dummy = head;
int count =0;
while(dummy != null){
stack.push(dummy.val);
count++;
dummy = dummy.next;
}
int[] arr = new int[count];
while(!stack.isEmpty()){
for (int i = 0; i < count ; i++) {
arr[i] =stack.pop();
}
}
return arr;
}
}
用递归
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);
}
}
作者:jyd
链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/solution/mian-shi-ti-06-cong-wei-dao-tou-da-yin-lian-biao-d/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
剑指 Offer 24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
递归
class Solution {
public ListNode reverseList(ListNode head) {
//@1:head刚开始不是0.到达尾结点停止.返回最后一个节点(不是返回null)
if(head ==null ||head.next ==null){
return head;
}
//此时的head.next是null.head是尾结点.之后才会执行@1
ListNode node = reserveList( head.next);
//下面的内容第一次执行时.从倒数第二个开始.倒数第二个节点的下一个节点(尾结点)的next= 倒数第二个节点.
//之后.这些归内容依次执行..
head.next.next =head;
head.next =null;
return node;
}
}
迭代
防止断链,至少放置三个指针,中间的node,后面的next,最前面的pre;如果要返回,
还需要一个最后面指向尾结点的指针reservenode;如果不用最后这个,可以返回pre;
public ListNode reverseList(ListNode head) {
//最前面的pre
ListNode prev = null;
//中间的node,
ListNode curr = head;
while(curr !=null){
//后面的next
ListNode next = curr.next;
// If
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}
例如这样:
还需要一个最后面指向尾结点的指针reservenode;如果不用最后这个,可以返回pre;
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev= null;
ListNode reverse= new ListNode(0);
ListNode curr = head;
while(curr !=null){
ListNode next = curr.next;
if(next ==null){
reverse = curr;
}
curr.next = prev;
prev = curr;
curr = next;
}
return reverse;
}
}
栈
class Solution {
Map<Integer, TreeNode> parent = new HashMap<Integer, TreeNode>();
Set<Integer> visited = new HashSet<Integer>();
public void dfs(TreeNode root) {
if (root.left != null) {
parent.put(root.left.val, root);
dfs(root.left);
}
if (root.right != null) {
parent.put(root.right.val, root);
dfs(root.right);
}
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
dfs(root);
while (p != null) {
visited.add(p.val);
p = parent.get(p.val);
}
while (q != null) {
if (visited.contains(q.val)) {
return q;
}
q = parent.get(q.val);
}
return null;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/solution/er-cha-shu-de-zui-jin-gong-gong-zu-xian-6fdt7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
剑指 Offer 25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例1:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->
迭代的算法:
方便理解,但是代码不简洁;
时间都是O(n)=M+N
空间是O(1)
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode margeList = new ListNode(-1);
if(l1==null){
return l2;
}else if(l2==null){
return l1;
}
if(l1.val<l2.val){
margeList= l1;
margeList.next= mergeTwoLists(l1.next,l2);
}else{
margeList =l2;
margeList.next= mergeTwoLists(l1,l2.next);
}
return margeList;
}
}
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
node.next= l1;
l1 =l1.next;
}else {
node.next =l2;
l2 =l2.next;
}
node =node.next;
}
if (l1 != null) {
node.next = l1;
}
if ((l2 != null)) {
node.next = l2;
}
return curr.next;
}
}
方法二:递归
时间:M+N
空间:M+N
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode margeList = new ListNode(-1);
if(l1==null){
return l2;
}else if(l2==null){
return l1;
}
if(l1.val<l2.val){
margeList= l1;
margeList.next= mergeTwoLists(l1.next,l2);
//注意,这里不能用if()替换到else{};这里要保证每次递归必须有一个放到形成的新的链表的元素
}else{
margeList =l2;
margeList.next= mergeTwoLists(l1,l2.next);
}
return margeList;
}
}
树
剑指 Offer 54. 二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第k大的节点。
说明:中序遍历的逆遍历
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
输出: 4
class Solution {
int vals =0;
int k=0;
public int kthLargest(TreeNode root, int k) {
if(root ==null||k<0){
return -1;
}
this.k = k;
//Deque<Integer> stack = new LinkedList<>();
dfs (root);
return vals;
}
void dfs(TreeNode root){
if(root == null)
return;
if(k==0){
return ;
}
dfs(root.right);
k--;
if(k==0){
vals = root.val;
}
dfs(root.left);
}
}
递归
剑指 Offer 10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n
级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
示例 2:
输入:n = 7
输出:21
class Solution {
public int numWays(int n) {
if( n ==1||n == 0){
return 1;
}
int a=1;
int b=1;
int c=0;
for(int i=2;i<=n;i++){
c=(a+b)%1_000_000_007;
a=b;
b=c;
}
return c;
}
}
剑指 Offer 10- I. 斐波那契数列
写一个函数,输入 n
,求斐波那契(Fibonacci)数列的第 n
项(即 F(N)
)。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
class Solution {
public int fib(int n) {
int []num= new int[]{0,1};
if(n<2){
return num[n];
}
int fOne =0;
int fTwo=1;
int fn =0;
for(int i=2;i<=n;++i){
fn =(fOne+fTwo)%1000000007;
fOne=fTwo;
fTwo=fn;
}
return fn;
这两道题的初值不同而已;
数组
剑指 Offer 03. 数组中重复的数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
说明:
在遍历中,交换每个数组的数字,让它和自己的下标对应的数字对齐;@1
当遍历到这个数字.他对应的下标不同,并且是与他应该对应的那个值是相同的,但是是已经对齐的.就赋值个a返回;@2
class Solution {
public int findRepeatNumber(int[] nums) {
if(nums.length<1){
return -1;
}
int a =0;
for(int i=0;i<nums.length;i++){
//(@1)
int temp =nums[i];
nums[i]=nums[temp];
nums[temp]= temp;
//@2
while(nums[i]!=i){
if(nums[i] ==nums[nums[i]]){
a=nums[i];
break;
}
}
}
return a;
}
}
剑指 Offer 39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
//1可以用一个hashmap遍历,记录,可以在idea跑,不知为啥不能在力扣跑全部的例子
hashmap的方法,时间和空间都是O(N)
2可以用摩尔投票法
时间是O(N);空间可以是O(1);
class Solution {
public int majorityElement(int[] nums) {
Map<Integer,Integer> map= new HashMap<>();
int c=0;
int b=1;
for(int i=0;i<nums.length;i++){
if(map.containsKey(nums[i])){
b++;
map.put(nums[i],b);
}else{
map.put(nums[i],b);
}
}
for (int a : map.keySet()
){
int value = map.get(a);
if(value>nums.length/2){
c=a;
}
}
System.out.println(c);
return c;
}
}
class Solution {
public int majorityElement(int[] nums) {
int x=0, votes=0;
for (int num : nums
) {
if (votes==0) x=num;
if(num==x){
votes=votes+1;
}else {
votes =votes-1;
}
}
return x;
}
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
解题思路:
- 利用快排思想的划分思想;用low和high左右交换,达到O(n)的时间复杂度;常数级别的空间复杂度;
- 最直接是想法就是发现是偶数, 拿出这个数字,其余的数组往前移动;空出末尾一个空间,把这个偶数放到末尾;
class Solution {
public int[] exchange(int[] nums) {
while(nums==null||nums.length < 0){
return new int[0];
}
int low=0;
int high=nums.length-1;
while(low<high){
while( low <high &&nums[low]%2!=0){
low++;
}
while(low <high && nums[high]%2==0){
high--;
}
if(low<high){
int temp= nums[low];
nums[low] = nums[high];
nums[high] = temp;
}
}
return nums;
}
}
位运算:
剑指 Offer 15. 二进制中1的个数
请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
(1)这种用于无符号的,在力扣运行,超时了;
//
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int count=0;
while(n!=0){
if((n&1)!=0){
count++;
}
n=n>>1;
}
return count;
}
}
(2)改变思路,不右移了;左移,并且可以做到不改变原来这个数的情况下进行,进行次数为32,int有关位数决定
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int count=0;
int flag=1;
while(flag!=0){
if((n&1)!=0){
count++;
}
flag=flag<<1;
n=n>>1;
}
return count;
}
}
(3)思路:
一个整数例如1100(12)减一为1011(11);显然,原来的数字的倒数第一个为1的数字变为0,之后的0全部变为1;
利用这个性质;可以:让这个数与减一之后的数字做与运算;能做多少次,就说明有多少个1;
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int count=0;
while(n!=0){
n = n&(n-1);
++count;
}
return count;
}
}
总结:
判断一个整数是2的整数次方,
- 如果是,例如(10)2,(100)4.这种,让他减一,在与其本身做与运算,若结果为0 ,则就是2的整数次方数;
输入两个整数m和n,计算需要改变m的二进制表示中的多少位才能得到n,
- 可以让两数做异或(^):
- 只有同为0或者同为1的数字异或是0;
- 利用这个,异或 之后,判断1的个数就行;
其他
1数值,位运算;
剑指 Offer 16. 数值的整数次方
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
若选择用一次次的循环,会超时
class Solution {
public double myPow(double x, int n) {
if(x==0){
return 0;
}
double res=1.0;
if(n<0){
x=1/x;
for(int i=0;i<Math.abs(n);i++){
res*=x;
}
return res;
}else{
}
for(int i=0;i<n;i++){
res*=x;
}
return res;
}
}
(2)快速幂:
拆解指数幂n,让n表示成位的形式,观察样式发现:当为0的时候此位的时结果乘1,若为1不为0的时候,此位的结果是该位置结果的1*x(该有的m);
注意事项:当m指数为负时,转换为整数,在int中,负的int比正的int 多1,需要转换成long型的;
复杂度:空间为1
时间应该是o(logn)
class Solution {
public double myPow(double x, int n) {
long b=n;
double res =1.0;
if(x==0) return 0;
if(b<0){
x=1/x;
b=-b;
}
//这里!=0和大于0都行,在多线程情况下,应该为大于0;
while(b>0){
if((b&1)==1) res*=x;
x*=x;
b=b>>1;
}
return res;
}
}
剑指 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]
(1)直接遍历的方法:
时间为o(10的N次方),用在了建立数列;
引用:
时间复杂度 O(10n)O(10^n)O(10n) : 生成长度为 10n10^n10n 的列表需使用 O(10n)O(10^n)O(10n) 时间。
空间复杂度 O(1)O(1)O(1) : 建立列表需使用 O(1)O(1)O(1) 大小的额外空间( 列表作为返回结果,不计入额外空间 )。
作者:jyd
链接:https://leetcode-cn.com/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof/solution/mian-shi-ti-17-da-yin-cong-1-dao-zui-da-de-n-wei-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public int[] printNumbers(int n) {
int len= 1;
for(int i=0;i<n;i++){
len*=10;
}
int[]array=new int[len-1];
for(int i=0;i<len-1;i++){
array[i]=i+1;
}
return array;
}
}
快速幂
应该差不
class Solution {
public int[] printNumbers(int n) {
if (n<=0){
return new int[0];
}
int res=1;
int x=10;
while(n>0){
if((n&1)==1) res*=x;
x*=x;
n=n>>1;
}
int len = res-1;
int[]A =new int[len];
for(int i=0;i<len;i++){
A[i]=i+1;
}
return A;
}
,2,3,4,5,6,7,8,9]
(1)直接遍历的方法:
时间为o(10的N次方),用在了建立数列;
引用:
时间复杂度 O(10n)O(10^n)O(10n) : 生成长度为 10n10^n10n 的列表需使用 O(10n)O(10^n)O(10n) 时间。
空间复杂度 O(1)O(1)O(1) : 建立列表需使用 O(1)O(1)O(1) 大小的额外空间( 列表作为返回结果,不计入额外空间 )。
作者:jyd
链接:https://leetcode-cn.com/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof/solution/mian-shi-ti-17-da-yin-cong-1-dao-zui-da-de-n-wei-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
```java
class Solution {
public int[] printNumbers(int n) {
int len= 1;
for(int i=0;i<n;i++){
len*=10;
}
int[]array=new int[len-1];
for(int i=0;i<len-1;i++){
array[i]=i+1;
}
return array;
}
}
快速幂
应该差不
class Solution {
public int[] printNumbers(int n) {
if (n<=0){
return new int[0];
}
int res=1;
int x=10;
while(n>0){
if((n&1)==1) res*=x;
x*=x;
n=n>>1;
}
int len = res-1;
int[]A =new int[len];
for(int i=0;i<len;i++){
A[i]=i+1;
}
return A;
}