剑指offer阅读(二)

<h2>剑指offer(二)</h2>

<h3>面试题九:斐波那契数列</h3>

<blockquote>
题目一:写一个函数,输入n,求斐波那契数列的第n项,裴波纳契数列的定义如下:
</blockquote>

$$f(n)=0 | n = 0$$
$$f(n)=1 | n = 1$$
$$f(n)=f(n-1)+f(n-2) | n > 1$$

<blockquote>
斐波那契数列使用循环效率比递归效率高,每一次函数调用,都需要在内存栈中分配空间保存参数
</blockquote>

<pre><code> public static long Fibonacci(int n) {
if (n < 0)
throw new IllegalArgumentException("invaild parameters");
if (n <= 1)
return n;
long fibNMinusOne = 1;
long fibNMinusTwo = 0;
long fibN = 0;
for (int i = 2; i <= n; ++i) {
fibN = fibNMinusTwo + fibNMinusOne;
fibNMinusTwo = fibNMinusOne;
fibNMinusOne= fibN;
}
return fibN;
}
</code></pre>

<blockquote>
题目二:一只青蛙一次可以跳上1级台阶,也可以跳上2级,求该青蛙跳上一个n级的台阶,总共有多少种跳法。
</blockquote>

第一次跳的时候有两种选择,跳1级和跳2级,可以换算成斐波那契数列。代码与上面相同。

<h3>面试题十:二进制中1的个数</h3>

<blockquote>
题目: 请实现一个函数,输入一个整数,输出该数二进制中1的个数。例如把9表示成二进制是1001,有两位是1,因此如果输入9,该函数输出2.
</blockquote>

常规解法:

<pre><code>int NumberOf(int n){
int count = 0;
unsigned int flag = 1;
while(flag){
if(n & flag)
count ++;
flag = flag<<1;
}
return count;
}
</code></pre>

惊喜解法:

<pre><code class="java">public static int numberOf(int n) {
int count = 0;
while (n != 0) {
count++;
n = n &(n-1);
}
return count;
}
</code></pre>

思路:把一个数减1,与之前的数,最后一位1就会变为0,有多少个1,就能做多少次与运算。

<h3>面试技巧</h3>

代码的完整性,从3个方面确保代码的完整性

  1. 功能测试
  2. 边界测试
  3. 负面测试

<h3>面试题十一:数值的整数n次方</h3>

<blockquote>
题目:实现函数double Power(double base,int exponent)求base得exponent次方,不使用库函数,同时不需要考虑大数问题
</blockquote>

公式
$a^n = a{n/2}·a{n/2} $ <strong>n为偶数</strong>
$an=a{(n-1)/2}·a^{(n-1)/2}·a$ <strong>n为奇数</strong>

<pre><code class="java">/**

  • Created by kangqizhou on 2017/1/30.
    */
    public class Offer11 {
    public static void main(String[] args) {
    Utils.syso(PowerWithExport(2, 2));
    }

    public static double PowerWithExport(double base, int exponent) {
    if (exponent == 0)
    return 1;
    if (exponent == 1)
    return base;
    double result = PowerWithExport(base, exponent >> 1);//无论是奇数还是偶数,右移以后都为n/2或者(n-1)/2
    result *= result;
    if ((exponent & 0x1) == 1) {//代表为奇数,再乘以base
    result *= base;
    }
    return result;
    }
    }

</code></pre>

<h3>面试题十二:打印从1到最大的n位数</h3>

<blockquote>
题目:输入数字n,按顺序打印出从1到最大的n位十进制数,比如输入3,则打印1,2,3一直到最大的3位数即999
</blockquote>

思路:不能直接使用int来循环,如果n是一个大数,很容易溢出,最直观的就是,使用数组中的每位来表示0到9的某一个字符。对于n位的字符,我们需要准备一个n+1的字符串

<pre><code>/**

  • Created by kangqizhou on 2017/1/30.
    */
    public class Offer12 {
    public static void main(String... args) {
    printNumbers(2);
    }

    public static void printNumbers(int n) {

    if(n < 1)
    throw new RuntimeException("The input number must larger than 0");
    int[] numbers = new int[n];
    for (int i = 0; i < n; i++) {
    numbers[i] = 0;
    }
    while (addOne(numbers) == 0) {
    printAllNumber(numbers);
    }
    }

    private static int addOne(int[] numbers) {
    int index = numbers.length;
    int flag = 1;
    do {
    index--;
    numbers[index] += flag;
    flag = numbers[index] / 10;
    numbers[index] %= 10;
    } while (index > 0 && flag != 0);
    if (index == 0 && flag == 1)
    return 1;
    return 0;
    }

    private static void printAllNumber(int[] numbers) {
    int index = 0;
    while (index < numbers.length && numbers[index] == 0) {
    index++;
    }
    for (int i = index; i < numbers.length; i++) {
    System.out.print(numbers[i]);
    }
    System.out.println("");
    }
    }

</code></pre>

<h4>递归解法</h4>

<pre><code>public static void printOneToNthDigits(int n, int[] arr) {
// 说明所有的数据排列选择已经处理完了
if (n >= arr.length) {
// 可以输入数组的值
printArray(arr);
} else {
// 对
for (int i = 0; i <= 9; i++) {
arr[n] = i;
printOneToNthDigits(n + 1, arr);
}
}
}
</code></pre>

<h3>面试题十三:在O(1)时间删除链表结点</h3>

<blockquote>
题目:给定单向链表的头指针和结点指针,定义一个函数,在O(1)时间删除该结点。链表结点与函数的定义如下:
</blockquote>

<pre><code class="cpp">struct ListNode{
int m_nValue;
ListNode* m_pNext;
};

void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted);
</code></pre>

思路:如果是中间的结点,用下一个结点的value和next覆盖掉当前的结点,如果是头结点,直接删除,如果是尾结点,就循环删除。不一定要真的释放掉目标结点的内存,可以释放目标节点的下个节点的内存。

<pre><code class="java">/**

  • Created by kangqizhou on 2017/1/30.
    */
    public class Offer13 {
    public static void main(String... args) {
    ListNode node = new ListNode();
    ListNode start = node;
    ListNode preDeleted = null;
    node.m_value = 0;
    for (int i = 1; i < 10; i++) {
    ListNode nextNode = new ListNode();
    nextNode.m_value = i;
    node.next = nextNode;
    node = nextNode;
    if (i == 5) {
    preDeleted = node;
    }
    }
    deleteNode(start, preDeleted);

    }

    private static void deleteNode(ListNode start, ListNode preDeleted) {
    if (start == null || preDeleted == null)
    return;
    if (preDeleted.next != null) {
    System.out.println("delete node " + preDeleted.m_value);
    ListNode node = preDeleted.next;
    preDeleted.m_value = node.m_value;
    preDeleted.next = node.next;
    node = null;
    } else if (start == preDeleted) {
    System.out.println("delete node " + start.m_value);
    }else {
    ListNode node = start;
    while (node.next!=preDeleted){
    node = node.next;
    }
    node.next = null;
    preDeleted = null;
    }
    }

    private static class ListNode {
    int m_value;
    ListNode next;
    }

}

</code></pre>

平均时间复杂度为$[(n-1)*O(1)+O(n)]/n$,结果还是O(1)

<h3>面试题十四:调整数组顺序使奇数位于偶数前面</h3>

<blockquote>
题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分
</blockquote>

<h4>初级解法</h4>

<pre><code class="java">import java.util.Arrays;

/**

  • Created by kangqizhou on 2017/1/31.
    */
    public class Offer14 {
    public static void main(String... args) {
    int a[] = {2, 1, 4, 5, 7, 3, 9, 3, 2, 4, 6, 8, 10};
    changeArray(a);
    Utils.syso(Arrays.toString(a));
    }

    private static void changeArray(int[] a) {
    if (a == null || a.length <= 0)
    return;
    int i = 0;
    int j = a.length - 1;
    while (i < j) {
    while (i < 6 && isOdd(a[i])) {
    i++;
    }
    while (j > 0 && !isOdd(a[j])) {
    j--;
    }
    if (i > j) {
    a[i] = a[i] + a[j];
    a[j] = a[i] - a[j];
    a[i] = a[i] - a[j];
    }

     }
    

    }
    //将独立的功能解耦,提高了代码的重用性
    private static boolean isOdd(int number){
    return number % 2==1;
    }

}

</code></pre>

<h3>面试题十五:链表中倒数第K个结点</h3>

<blockquote>
题目:输入一个链表,输出该链表中倒数第K个结点,为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第一个节点,例如一个链表有6个结点,从头结点开始,他们的值依次是1、2、3、4、5、6.这个链表倒数第3个结点是值为4的结点。
链表结点的定义如下
</blockquote>

<pre><code class="cpp">stuct ListNode{
int m_nValue;
ListNode* m_pNext;
}
</code></pre>

<pre><code class="java">/**

  • Created by kangqizhou on 2017/1/31.
    */
    public class Offer15 {
    public static void main(String... args) {
    ListNode start = new ListNode();
    ListNode temp = start;
    start.value = 0;
    for (int i = 1; i < 10; i++) {
    ListNode node = new ListNode();
    node.value = i;
    temp.next = node;
    temp = node;
    }
    Utils.syso(getValue(start, 2).value);
    }

    private static ListNode getValue(ListNode start, int k) {
    if (start == null || k <= 0)
    return null;
    ListNode index1Node = start;
    for (int i = 0; i < k - 1; i++) {
    if (index1Node.next != null) {
    index1Node = index1Node.next;
    } else {
    return null;
    }
    }
    ListNode index2Node = start;
    while (index1Node.next != null) {
    index1Node = index1Node.next;
    index2Node = index2Node.next;
    }
    return index2Node;
    }

    private static class ListNode {
    int value;
    ListNode next;
    }
    }

</code></pre>

<h4>思路</h4>

定义两个指针,第一个移动到k-1时,开始移动第一个指针。直到第二个的next为null。

<h3>面试题十六:反转链表</h3>

<blockquote>
题目:定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。
</blockquote>

<pre><code>/**

  • Created by kangqizhou on 2017/1/31.
    */
    public class Offer16 {
    public static void main(String... args){
    Utils.syso(reverseList(ListNode.getSample()).value);
    }

    public static ListNode reverseList(ListNode start){
    if (start==null)
    return null;
    ListNode temp1 = start;
    ListNode temp2 = null;
    ListNode newStart = null;
    while (temp1!=null){
    ListNode temp3 = temp1.next;
    if (temp3==null)
    newStart = temp1;
    temp1.next = temp2;
    temp2 = temp1;
    temp1 = temp3;
    }
    return newStart;
    }
    }

</code></pre>

<h3>面试题十七: 合并两个排序的链表</h3>

<blockquote>
题目:输入两个递增排序的列表,合并这两个链表并使新链表并使新链表中的节点仍然是按照递增排序的。例如输入图3.7中的链表1和链表2,则合并之后的升序链表如链表3所示。链表结点定义如下:
</blockquote>

<pre><code>private static ListNode merge(ListNode node1, ListNode node2) {
if (node1 == null || node2 == null)
throw new NullPointerException("invaild arguments");
ListNode start = new ListNode();
ListNode temp = start;
while (node1 != null && node2 != null) {
if (node1.value < node2.value) {
temp.value = node1.value;
node1 = node1.next;
} else {
temp.value = node2.value;
node2 = node2.next;
}
temp.next = new ListNode();
temp = temp.next;
}
if (node2!=null)
temp.value = node2.value;
else temp.value = node1.value;
return start;
}
</code></pre>

<h3>面试题十八:树的子结构</h3>

<blockquote>
题目:输入两棵二叉树A和B,判断B是不是A的子结构。二叉树结点的定义如下
</blockquote>

<pre><code class="cpp">struct BinaryTreeNode{
int m_nValue;
BinaryTreeNode* left;
BinaryTreeNode* right;
}
</code></pre>

<pre><code class="java"> public static boolean HasSubTree(TreeNode root, TreeNode root2) {
boolean result = false;
if (root != null && root2 != null) {
if (root.value == root2.value) {
result = doesTree1HaveTree2(root, root2);
}
if (!result)
result = HasSubTree(root.left, root2);
if (!result)
result = HasSubTree(root.right, root2);
}
return result;
}

private static boolean doesTree1HaveTree2(TreeNode root, TreeNode root2) {
    if (root2 == null)
        return true;
    if (root == null)
        return false;
    if (root.value != root2.value)
        return false;
    return doesTree1HaveTree2(root.left, root2.left) && doesTree1HaveTree2(root.right, root2.right);
}

</code></pre>

<h3>面试题十九:二叉树的镜像</h3>

<blockquote>
题目:请完成一个函数,输入一个二叉树,该函数输出他的镜像。
</blockquote>

<pre><code class="java"><br /> public static void mirrorRecursively(TreeNode node) {
if (node == null)
return;
if (node.left == null && node.right == null)
return;
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
if (node.left!=null)
mirrorRecursively(node.left);
if(node.right!=null)
mirrorRecursively(node.right);
}
</code></pre>

<h3>面试题二十:顺时针打印矩阵</h3>

<blockquote>
题目:输入一个矩阵,按照 从外向里以顺时针的顺序一次打印出每一个数字。例如:如果输入如下矩阵:
</blockquote>

<pre><code class="java">/**

  • Created by kangqizhou on 2017/2/1.
    */
    public class Offer20 {
    public static void main(String... args) {
    int[][] a = {{1, 2, 3, 4, 5, 6}, {2, 3, 4, 5, 6, 7}, {7, 8, 9, 10, 11, 12}};
    printMatrix(a);
    }

    private static void printMatrix(int[][] a) {
    if (a == null)
    return;
    int start = 0;
    while (a.length > start * 2 && a[0].length > start * 2) {
    printMatrixInCircle(a, start);
    start++;
    }
    }

    private static void printMatrixInCircle(int[][] a, int start) {
    int endX = a[0].length - 1 - start;
    int endY = a.length - 1 - start;
    for (int i = start; i <= endX; i++) {
    System.out.println(a[start][i]);
    }
    if (start < endX)
    for (int i = start + 1; i <= endY; i++) {
    System.out.println(a[i][endX]);
    }

     if (start < endX && start < endY)
         for (int i = endX - 1; i >= start; i--) {
             System.out.println(a[endY][i]);
         }
     if (start < endX && start < endY - 1)
         for (int i = endY - 1; i >= start + 1; i--) {
             System.out.println(a[i][start]);
         }
    

    }
    }

</code></pre>

<h3>面试题二十一:包含min函数的栈</h3>

<blockquote>
题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数,在该栈中,调用min,push,pop的时间复杂度都是O(1)
</blockquote>

<pre><code>/**

  • Created by kangqizhou on 2017/2/1.
    */
    public class Offer21 {
    public static void main(String... args) {
    Stack stack = new Stack();
    stack.push(2);
    stack.push(3);
    stack.push(4);
    stack.push(1);
    stack.push(2);
    System.out.println(stack.min());
    stack.pop();
    stack.pop();
    System.out.println(stack.min());
    }

    public static class Stack {
    private java.util.Stack<Integer> stack = new java.util.Stack<>();
    private java.util.Stack<Integer> supportStack = new java.util.Stack<>();

     public int push(int i) {
         if (supportStack.isEmpty())
             supportStack.push(i);
         else if (supportStack.peek() > i)
             supportStack.push(i);
         else
             supportStack.push(supportStack.peek());
         stack.push(i);
         return i;
     }
    
     public int pop() {
         supportStack.pop();
         return stack.pop();
     }
    
     public int min() {
         return supportStack.peek();
     }
    

    }
    }
    </code></pre>

<h3>面试题二十二:栈的压入、弹出序列</h3>

<blockquote>
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压站序列,序列4,5,3,2,1是该栈的一个弹出序列,而4,3,5,1,2则不可能是该压站序列对应的弹出序列。
</blockquote>

<pre><code class="java">import java.util.Stack;

/**

  • Created by kangqizhou on 2017/2/1.
    */
    public class Offer22 {
    public static void main(String... args) {
    int[] a = {1, 2, 3, 4, 5};
    int[] b = {4, 3, 5, 1, 2};
    int[] c = {4, 3, 5, 2, 1};
    Utils.syso(isOrder(a, c));
    }

    public static boolean isOrder(int[] a, int[] b) {
    if (a == null || b == null || a.length != b.length)
    return false;
    Stack<Integer> stack = new Stack<>();
    int index1 = 0;
    int index2 = 0;
    while (index1 < a.length || index2 < b.length) {
    if (!stack.isEmpty() && stack.peek() == b[index2]) {
    stack.pop();
    index2++;
    } else if (index1 < a.length) {
    stack.push(a[index1]);
    index1++;
    } else
    return false;
    }
    return true;
    }
    }

</code></pre>

<h3>面试题二十三:从上到下打印二叉树</h3>

<blockquote>
题目:从上到下打印出二叉树的每一个节点,同一层的结点按照从左到右的顺序打印。例如输入图4.5中的二叉树,则一次打印出8,6,10,5,7,9,11。
</blockquote>

思路:使用一个链表,将结点逐层添加入数据结构,然后遍历。

<pre><code>import java.util.LinkedList;

/**

  • Created by kangqizhou on 2017/2/1.
    */
    public class Offer23 {
    public static void main(String... args) {
    TreeNode node = new TreeNode();
    node.value = 8;
    TreeNode node1 = new TreeNode();
    node1.value = 6;
    node.left = node1;
    TreeNode node2 = new TreeNode();
    node2.value = 10;
    node.right = node2;
    TreeNode node3 = new TreeNode();
    node1.left = node3;
    node3.value = 5;
    TreeNode node4 = new TreeNode();
    node4.value = 7;
    node1.right = node4;
    TreeNode node5 = new TreeNode();
    node5.value = 9;
    TreeNode node6 = new TreeNode();
    node6.value = 11;
    node3.left = node5;
    node3.right = node6;
    getOrder(node);
    }

    public static void getOrder(TreeNode node) {
    LinkedList<TreeNode> linkedList = new LinkedList<>();
    linkedList.add(node);
    while (!linkedList.isEmpty()) {
    TreeNode node1 = linkedList.removeFirst();
    if (node1 != null) {
    linkedList.add(node1.left);
    linkedList.add(node1.right);
    Utils.syso(node1.value);
    }
    }
    }
    }

</code></pre>

<h3>面试题二十四:二叉搜索树的后序遍历序列</h3>

<blockquote>
题目:输入一个整数数组,判断该数组是不是某二叉搜索数的后序遍历的结果,如果输入的数组是{7,4,6,5},由于没有哪棵二叉树的后序遍历的结果就是这个序列,因此返回false;
</blockquote>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容