16.数值的整数次方
思路:考虑特殊情况(1)底数为0;(2)指数为0;(3)指数为负数。时间复杂度O(logn)。
class Solution {
public double Power(double base, int exponent) {
double result;
if(base==0) return 0;
else if(exponent==0) return 1;
else if(exponent<0){
result = 1/powerCore(base, -exponent);
}
else result = powerCore(base, exponent);
return result;
}
public double powerCore(double base, int exponent){
if(exponent==0) return 1;
if(exponent==1) return base;
double result = powerCore(base, exponent>>1);
if(exponent % 2 == 1) result = result * result * base;
else result = result * result;
return result;
}
}
17. 打印从1到最大的n位数
思路:大数问题,全排列,利用递归来做
public void print1ToMaxOfNDigits(int n) {
if (n <= 0)
return;
char[] number = new char[n];
print1ToMaxOfNDigits(number, 0);
}
private void print1ToMaxOfNDigits(char[] number, int digit) {
if (digit == number.length) {
printNumber(number);
return;
}
for (int i = 0; i < 10; i++) {
number[digit] = (char) (i + '0');
print1ToMaxOfNDigits(number, digit + 1);
}
}
private void printNumber(char[] number) {
int index = 0;
while (index < number.length && number[index] == '0')
index++;
while (index < number.length)
System.out.print(number[index++]);
System.out.println();
}
**18. 删除链表的节点
18-1. O(1)时间内删除链表节点 **
思路:时间复杂度要求较高,我们只能修改链表中指针的值。处理两种特殊情况(1)头结点(2)尾结点:仍要按顺序查找
public ListNode deleteNode(ListNode head, ListNode tobeDelete) {
if (head == null || tobeDelete == null)
return null;
if (tobeDelete.next != null) {
// 要删除的节点不是尾节点
ListNode next = tobeDelete.next;
tobeDelete.val = next.val;
tobeDelete.next = next.next;
} else {
if (head == tobeDelete)
// 只有一个节点
head = null;
else {
ListNode cur = head;
while (cur.next != tobeDelete)
cur = cur.next;
cur.next = null;
}
}
return head;
}
18.2 删除链表中重复的节点
思路:#设置两个节点(1)pre指向重复节点段的前一个节点(2)now指向重复节点段的下一个节点,利用两个指针之间的距离,如果距离大于一,说明有重复段,如果距离不大于一,说明没有重复段。#注意,虚拟头结点dummy的使用可以使得无需考虑头节点被删除,但要记得利用构造函数赋初值。时间复杂度O(n)。
class Solution {
public ListNode deleteDuplication(ListNode head) {
ListNode dummy = new ListNode(-1);
dummy.next = head;//虚拟头结点,不需考虑删除头结点的问题。
ListNode pre = dummy;
while(pre!=null && pre.next!=null){
ListNode now = pre.next;
while(now!= null && pre.next.val == now.val) now = now.next;//now走一步,如果有重复,now走多步。now永远指向重复节点的下一个节点。
if(pre.next.next == now) pre = pre.next;// 如果now只走了一步,说明这次没有重复,pre也前进一步
else pre.next = now;//否则,pre不前进,pre的下一个就是now
}
return dummy.next;
}
}
19. 正则表达式匹配
思路:(1)动态规划:两个字符串是否匹配,多用动态规划,节省内存。子问题的解存起来。O(mn)(2)递归:正常递归
class Solution {
enum RESULT{
TRUE,FALSE;
}//定义一个枚举类型而不用boolean,因为可以有null这种情况;boolean只有true,false两种,不能用来记录是否存储过。
public RESULT[][] f;//f用来记录是否存储过,如果存储过那么s[i]...s[n]是否与p[j]...p[n]匹配
public boolean isMatch(String s, String p) {
f = new RESULT[s.length()+1][p.length()+1];
return dp(0, 0, s, p);//dp
}
public boolean dp(int m, int n, String s, String p){
int sSize = s.length();
int pSize = p.length();
boolean ans;//用来记录return 的值
if(f[m][n]!=null) return f[m][n]==RESULT.TRUE;//访问过,直接返回存储的值。
if(n == pSize){//如果p到了最后一位,s是否到最后一位,如果不到最后一位,说明有没匹配上的,return false
ans = m==sSize;
f[m][n] = ans?RESULT.TRUE:RESULT.FALSE;
return ans;
}
boolean firstEqual = m<sSize && (s.charAt(m)==p.charAt(n) || p.charAt(n)=='.');//比较当前字符号是否相等(字符相等或者.)
if(n+1<pSize && p.charAt(n+1)=='*'){//如果是下一个字符是*,考虑重复0次,重复>=1次
ans = dp(m,n+2,s,p) || firstEqual&&dp(m+1,n,s,p);
}
else{
ans = firstEqual && dp(m+1,n+1,s,p);//普通字符或者.
}
f[m][n] = ans?RESULT.TRUE:RESULT.FALSE;//记录是否相等,并且已经访问过
return ans;//返回是否相等
}
}
20.表示数值的字符串
思路:将数字的形式总结为:(A.B E/e A) ,按顺序进行判断(A代表带符号整数,B代表不带符号整数)
class Solution {
public boolean isNumber(String s) {
if(s.length()<=0 ) return false;
int[] index = new int[1];//传引用,方便修改index
index[0] = 0;
boolean numeric = scanInteger(s, index);
if(index[0]<s.length() && s.charAt(index[0])=='.'){
index[0]++;
numeric = scanUnsigned(s, index)||numeric;
}
if(index[0]<s.length() && (s.charAt(index[0])=='E' || s.charAt(index[0])=='e')){
index[0]++;
numeric = scanInteger(s, index)&& numeric;
}
if(index[0]==s.length()) return numeric;
return false;
}
public boolean scanInteger(String s, int[] index){
if(index[0] < s.length() && (s.charAt(index[0])=='+' || s.charAt(index[0])=='-'))
index[0]++;
return scanUnsigned(s, index);
}
public boolean scanUnsigned(String s, int[] index){
int begin = index[0];
while(index[0] < s.length()){
if(s.charAt(index[0]) >= '0' && s.charAt(index[0]) <='9') index[0]++;
else break;
}
if(begin<index[0]) return true;
else return false;
}
}
21. 调整数组顺序使奇数位于偶数前面
思路(1)前后两个index, 同时遍历,做交换。注意位运算优先级,==运算优先级高于&,记得加括号。
class Solution {
public void reOrderArray(int [] array) {
int begin = 0;
int end = array.length-1;
while(begin < end ){
if((array[begin]&1)==1 && (array[end]&1)==0){
begin++;
end--;
}
else if((array[begin]&1)==1 && (array[end]&1)==1){
begin++;
}
else if((array[begin]&1)==0 && (array[end]&1)==0){
end--;
}
else{
int tmp = array[begin];
array[begin] = array[end];
array[end] = tmp;
begin++;
end--;
}
}
}
}
22. 链表中倒数第k个节点
思路:要求只遍历链表一次,两个指针,before,after;after先走k-1步,两个指针再一起走,当after最后一个,before就是要返回的倒数第k个。注意特殊情况,k<链表长度;链表为空。
class Solution {
public ListNode findKthToTail(ListNode pListHead, int k) {
ListNode before = pListHead;
ListNode after = pListHead;
if(pListHead == null) return null;
for(int i=0; i<k-1; i++){
if(after.next != null) after = after.next;
else return null;
}
while(after.next != null){
after = after.next;
before = before.next;
}
return before;
}
}
23.链表中环的入口节点
思路:(1)先判断是否有环,有环返回相遇点;相遇点再次走到相遇点的距离,即为环的长度;p1,p2,p2先走出环的长度,p1p2再一起走,相遇点,即为环的入口节点(2)巧妙思路:p2走两步,p1走一步,记录相遇的点。p2回到起点,p1从相遇点出发,步速都为1,再次相遇的点即为环的入口。证明如下:
**
class Solution {
public ListNode entryNodeOfLoop(ListNode head) {
ListNode meetingNode = isCircle(head);
if(meetingNode == null) return null;
int count = 1;
ListNode cur = meetingNode;
while(cur.next!=meetingNode){
count++;
cur = cur.next;
}
ListNode before = head;
ListNode after = head;
for(int i=0; i<count;i++){
after = after.next;
}
while(after!=before){
after = after.next;
before = before.next;
}
return before;
}
public ListNode isCircle(ListNode head){
if(head==null) return null;
ListNode slow = head.next;
if(slow==null) return null;
ListNode fast = slow.next;
while(fast!=null){
if(fast==slow) return slow;
slow = slow.next;
fast = fast.next;
if(fast!=null) fast = fast.next;
}
return null;
}
}
class Solution {
public ListNode entryNodeOfLoop(ListNode head) {
if (head == null || head.next == null)
return null;
ListNode slow = head.next;
if(slow==null) return null;
ListNode fast = slow.next;
while (slow != fast){
if(fast!=null){
fast = fast.next.next;
slow = slow.next;
}
else return null;
}
fast = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
24.翻转链表
思路:虚拟头结点
public ListNode reverseList(ListNode head) {
if(head==null) return null;
ListNode dummy = new ListNode(-1);
dummy.next = null;
while(head!=null){
ListNode tmp = dummy.next;
dummy.next = head;
head = head.next;
dummy.next.next = tmp;
}
return dummy.next;
}
25.合并两个排序的链表
思路:设置虚拟头结点,并判断特殊情况。
public ListNode merge(ListNode l1, ListNode l2) {
if(l1==null && l2==null) return null;
if(l1==null) return l2;
if(l2==null) return l1;
ListNode dummy = new ListNode(-1);
ListNode cur = dummy;
while(l1!=null && l2!=null){
if(l1.val<l2.val){
cur.next = l1;
l1 = l1.next;
cur = cur.next;
}
else{
cur.next = l2;
l2 = l2.next;
cur = cur.next;
}
}
if(l1!=null) cur.next=l1;
if(l2!=null) cur.next=l2;
return dummy.next;
}
26. 树的子结构:
思路:用递归解决。先判断以当前节点为根节点的树A是否包含B;如果不包含,判断左节点有没有子树,包不包含B;否则,判断右子树。
class Solution {
public boolean hasSubtree(TreeNode pRoot1, TreeNode pRoot2) {
boolean result = false;
if(pRoot1!=null && pRoot2!=null){//根节点不为空
if(Math.abs(pRoot1.val-pRoot2.val)<1e-6)//根节点相等
result = isAHaveB(pRoot1, pRoot2);//如果A包含B,返回result;
if(result != true)//如果不包含,去查左节点
result = hasSubtree(pRoot1.left, pRoot2);
if(result != true)//左节点还不包含,去查右节点
result = hasSubtree(pRoot1.right, pRoot2);
}
return result;
}
public boolean isAHaveB(TreeNode p1, TreeNode p2){
if(p2==null) return true;
if(p1==null) return false;
if(Math.abs(p1.val-p2.val)<1e-6)
return (isAHaveB(p1.left,p2.left) && isAHaveB(p1.right, p2.right));
return false;
}
}