声明:本文参照——剑指Offer——编程题的Java实现,并对一些算法进行优化,以下简称《参考》。
面试题11:数值的整数次方
题目大致为:
实现函数double power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
实现函数double power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
思路:
可以考虑对指数折半,这样只需要计算一半的值,若指数是奇数,则-1再折半,否则直接折半。
【注意】《参考》中未考虑指数为负数的情况。
Java实现
public class MainTest {
public static void main(String[] args) {
int base = 3;
int exponent = -3;
System.out.println(power(base, exponent));
exponent = 6;
System.out.println(power(base, exponent));
}
public static double power(double base, int exponent) {
if (exponent == 0) return 1;
if (exponent < 0) return 1 / power(base, -exponent);
if (exponent % 2 == 0) {
double temp = power(base, exponent >> 1);
return temp * temp;
} else {
double temp = power(base, (exponent - 1 )>> 1);
return temp * temp * base;
}
}
}
运行结果:
0.037037037037037035
729.0
面试题13:在O(1)时间删除链表结点
题目大致为:
给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。
思路:
想要在O(1)时间内删除链表的指定结点,要遍历的话得O(n),则肯定不能遍历。若是要删除的结点不是尾结点,那么可以将后面的那个值复制到该指针处,并将后面指针所指空间删除,用复制+删除后面的实现删除,时间复杂度为O(1)。对于尾结点,需要遍历,那么时间复杂度是O(n),但是总的时间复杂度为[(n-1)*O(1)+O(n)]/n,结果是O(1)。
【注意】链表只有一个节点,切要删除该节点(即头结点),是无法办到的,这点在《参考》中未提及。
package cn.learn.test;
public class MainTest {
public static void main(String[] args) {
// 构建链表
ListNode head = new ListNode(1);
ListNode node_2 = new ListNode(2);
ListNode node_3 = new ListNode(3);
ListNode node_4 = new ListNode(4);
ListNode node_5 = new ListNode(5);
ListNode node_6 = new ListNode(6);
ListNode node_7 = new ListNode(7);
head.setNext(node_2);
node_2.setNext(node_3);
node_3.setNext(node_4);
node_4.setNext(node_5);
node_5.setNext(node_6);
node_6.setNext(node_7);
node_7.setNext(null);
// 输出原始链表
System.out.println("原始链表:");
printList(head);
System.out.println("----------------");
// 删除结点node_3
deleteNode(head, node_3);
System.out.println("删除node_3后链表:");
printList(head);
System.out.println("----------------");
// 删除结点head
deleteNode(head, head);
System.out.println("删除head后链表:");
printList(head);
System.out.println("----------------");
}
public static void deleteNode(ListNode head, ListNode toBeDeleted) {
if (toBeDeleted == null || head == null) return;
if (toBeDeleted.next != null) { //删除的是中间节点
ListNode temp = toBeDeleted.next;
toBeDeleted.value = temp.value;
toBeDeleted.next = temp.next;
}
// 【注意】这部分代码不起作用,故注释了。
// else if (head == toBeDeleted) {
// // 如果头结点就是要删除的节点
// head = null;
// }
else {
ListNode temp = head;
while (temp.next != toBeDeleted) {
temp = temp.next;
}
temp.next = null;
}
}
/**
* 打印链表
*
* @param head头指针
*/
public static void printList(ListNode head) {
ListNode current = head;
while (current != null) {
System.out.print(current.getValue() + "、");
current = current.getNext();
}
System.out.println();
}
}
class ListNode {
int value;
ListNode next;
public ListNode(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public ListNode getNext() {
return next;
}
public void setNext(ListNode next) {
this.next = next;
}
}
运行结果:
原始链表:
1、2、3、4、5、6、7、
----------------
删除node_3后链表:
1、2、4、5、6、7、
----------------
删除head后链表:
2、4、5、6、7、
----------------
面试题14:调整数组顺序使奇数位于偶数前面
题目大致为:
对于一个数组,实现一个函数使得所有奇数位于数组的前半部分,偶数位于数组的后半部分。
思路:
可以使用双指针的方式,一个指针指向数组的开始,一个指针指向数组的尾部,如果头部的数为偶数且尾部的数是奇数则交换,否则头部指针向后移动,尾部指针向前移动,直到两个指针相遇
【注意】这里没有要求调整前后奇数的相对位置和偶数相对位置一致。
Java代码:
public class MainTest {
public static void main(String[] args) {
int arr[] = {1, 2, 3, 4, 5, 6};
rejectArray(arr);
System.out.println(Arrays.toString(arr));
}
public static void rejectArray(int[] arr) {
int l = 0, r = arr.length - 1;
while (l < r) {
if (arr[l] % 2 == 1) {
l++;
} else if (arr[l] % 2 == 0 && arr[r] % 2 == 1) {
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
l++;
r--;
} else {
r--;
}
}
}
}
运行结果:
[1, 5, 3, 4, 2, 6]
面试题15:链表中倒数第k个结点
题目大致为:
在一个链表中,查找倒数的第k个数。
思路:
使用双指针的方式,前一个指针先走k步(中间隔k-1个结点),后一个指针才开始走,直到第一个指针走到尾,后一个指针指向的就是要找的倒数第k个数。值得注意的是:1、k是否超过链表长度且k必须为正整数;2、链表是否为空。
Java代码:
public class MainTest {
public static void main(String[] args) {
// 构建链表
ListNode head = new ListNode(1);
ListNode h1 = new ListNode(2);
ListNode h2 = new ListNode(3);
ListNode h3 = new ListNode(4);
ListNode h4 = new ListNode(5);
ListNode h5 = new ListNode(6);
head.setNext(h1);
h1.setNext(h2);
h2.setNext(h3);
h3.setNext(h4);
h4.setNext(h5);
h5.setNext(null);
// 查找倒数第k个
ListNode p = findKthToTail(head, 3);
System.out.println(p.getValue());
}
public static ListNode findKthToTail(ListNode head, int k) {
if (head == null || k <= 0) {
return null;
}
ListNode prePoint = head;
ListNode postPost = head;
while (k-- > 0) {
prePoint = prePoint.next;
if (prePoint == null) {
return null;
}
}
while (prePoint != null) {
prePoint = prePoint.next;
postPost = postPost.next;
}
return postPost;
}
}
class ListNode {
int value;
ListNode next;
public ListNode(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public ListNode getNext() {
return next;
}
public void setNext(ListNode next) {
this.next = next;
}
}
运行结果:
4
面试题16:反转链表
题目大致为:
对于一个链表,反转该链表并返回头结点。
思路:
主要是指针的操作,但是要注意不能断链。这里可以使用非递归的方式求解。
Java代码
public class MainTest {
public static void main(String[] args) {
// 构建链表
ListNode head = new ListNode(1);
ListNode h1 = new ListNode(2);
ListNode h2 = new ListNode(3);
ListNode h3 = new ListNode(4);
ListNode h4 = new ListNode(5);