剑指offer刷题笔记(三)
面试题16. 数值的整数次方
- 实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
示例 1:
输入: 2.00000, 10
输出: 1024.00000
示例 2:
输入: 2.10000, 3
输出: 9.26100
示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25
- 说明:
-100.0 < x < 100.0
n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。
思路:
这个问题主要解决还是时间复杂度,我们需要了解计算机中加减运算的原理,,用右移运算符代替除以二,用位与运算符&代替求余操作在效率上会高很多.另外,在求一个数的整数次方,例如2的32次方时,我们可以将其替换成2的16次方的平方,进而2的8次的平方,用递归来实现这一操作.
class Solution {
public double myPow(double x, int n) {
if(x==0.0&&n<0){
return 0;
}
long i=n;
if(n<0){
i=-(long)n;
}
double result=powerResult(x,i);
if(n<0){
result=1/result;
}
return result;
}
public double powerResult(double x,long n){
if(n==0){
return 1;
}
if(n==1){
return x;
}
double result=powerResult(x,n>>1);
result*=result;
if((n&1)==1){
result*=x;
}
return result;
}
}
面试题17. 打印从1到最大的n位数
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]
说明:
用返回一个整数列表来代替打印
n 为正整数
这种方法是最容易想到的方法,但没有考虑到大数问题.
class Solution {
public int[] printNumbers(int n) {
double max = Math.pow(10, n);
int len = (int) max-1;
int [] res = new int [len];
for(int i=0;i
part2: 考虑到大数问题,用字符数组来存储得到的结果.如果我们在结果数组前面补0,就会发现其实就是数字从0-9的全排列问题,打印时0不打印即可
class Solution {
public void printNumbers(int n) {
char[] a=new char[n+1];
if(n<=0){
return;
}
a[n]='\0';
for(int i=0;i<10;i++){
a[0]=(char)('0'+i);
prints(a,n,0);
}
}
public void prints(char[] a,int length,int index){
if(index==length-1){
printa(a);
return;
}
for(int i=0;i<10;i++){
a[index+1]=(char)(i+'0');
prints(a,length,index+1);
}
}
public void printa(char[] a){
boolean isbeginning=true;
for(int i=0;i
面试题19. 正则表达式匹配
请实现一个函数用来匹配包含'. '和''的正则表达式。模式中的字符'.'表示任意一个字符,而''表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 '*',无连续的 0'*'。
分析字符串,当模式字符串中字符是'.'时,可以匹配任意一个字符,如果不是‘.’,而且匹配串中的字符也跟他相互匹配,也往下走,最复杂的情况是'',碰见''时,一种选时模式串往后两个字符,相当于被忽略,一种是字符串向后移动一个字符,这时模式串可以向后移动两个字符,也可以不移动。我第一次提交时将两种情况都包含进去,后来发现超时了,看了题解中的解释才明白,字符串向后移动欧诺个,模式串可以不移动,因为模式串向后移动两个字符已经被包括在第一次的选择中了,相当于我们进行了重复操作。
public class test {
public static void main(String[] args) {
String s="aaaaaaaaaaaaab";
String p="a*a*a*a*a*a*a*a*a*a*c";
Solution a=new Solution();
System.out.println(a.isMatch(s,p));
}
}
class Solution {
public boolean isMatch(String s, String p) {
if (s.length() == 0) {
if (p.length() % 2 != 0) {
return false;
}
int i = 1;
while (i < p.length()) {
if (p.charAt(i) != '*') {
return false;
}
i += 2;
}
return true;
}
if(p.length()==0){
return false;
}
char a1 = s.charAt(0);
char a2 = p.charAt(0);
char a3 = 'a';
if (p.length() > 1) {
a3 = p.charAt(1);
}
if (a3 == '*') {
if (a1 == a2 || a2 == '.') {
return isMatch(s.substring(1), p)||isMatch(s,p.substring(2));
} else {
return isMatch(s, p.substring(2));
}
}
if (a1 == a2 || a2 == '.') {
return isMatch(s.substring(1), p.substring(1));
}
return false;
}
}
面试题20. 表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、"5e2"、"-123"、"3.1416"、"0123"都表示数值,但"12e"、"1a3.14"、"1.2.3"、"+-5"、"-1E-16"及"12e+5.4"都不是。
class Solution {
public boolean isNumber(String s) {
if(s==null||s.length()==0){
return false;
}
boolean numbool=false;
boolean dotbool=false;
boolean ebool=false;
char[] a=s.trim().toCharArray();
for(int i=0;i='0'&&a[i]<='9'){
numbool=true;
}
else if(a[i]=='.'){
if(dotbool||ebool){
return false;
}
else{
dotbool=true;
}
}
else if(a[i]=='e'||a[i]=='E'){
if(!numbool||ebool){
return false;
}
else{
ebool=true;
numbool=false;
}
}
else if(a[i]=='+'||a[i]=='-'){
if(i!=0&&a[i-1]!='e'&&a[i-1]!='E'){
return false;
}
}
else{
return false;
}
}
return numbool;
}
}
面试题21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
用位与运算符来代替求余操作,剩下就是两头遍历交换了。
class Solution {
public int[] exchange(int[] nums) {
if(nums==null||nums.length==0){
return nums;
}
int i=0;
int j=nums.length-1;
while(i
面试题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==0){
return null;
}
ListNode aListNode=head;
ListNode bListNode=head;
for(int i=0;i
面试题24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
在反转过程中为了防止链表断链而导致的数据丢失,我们需要先将断链后的结果存储起来。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode headNode=head;
ListNode preNode=null;
ListNode reserveNode=null;
while(headNode!=null){
ListNode nextNode=headNode.next;
if(nextNode==null){
reserveNode=headNode;
}
headNode.next=preNode;
preNode=headNode;
headNode=nextNode;
}
return reserveNode;
}
}
面试题25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
思路就是递归操作,更多的需要注意非法输入和空链表的判断。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}
if(l2==null){
return l1;
}
ListNode l=null;
if(l1.val