剑指offer阅读(三)

2017-04-12  本文已影响293人  桥寻

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

<h3>面试题二十五:二叉树中和为某一值的路径</h3>

<blockquote>
题目:输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。二叉树的定义如下:
</blockquote>

<pre><code class="java">class BinartTreeNode{
int m_nValue;
BinaryTreeNode left;
BinaryTreeNode right;
}
</code></pre>

<pre><code class="java">package offer;

import java.util.LinkedList;

/**

}

</code></pre>

思路:使用一个链表把所有路过的点都记录下来,使用前序遍历,当退出当前节点的时候,removelast,函数栈的思想。

<h3>面试题二十六:复杂链表的复制</h3>

<blockquote>
题目:请实现函数ComplexListNode* Clone(ComplexListNode* head)复制一个复杂链表,在复杂链表中,每个节点除了有一个next的指针指向下一个节点外,还有一个sibling指向链表中的任意节点或者NULL。节点的C++定义如下:
</blockquote>

<pre><code>struct ComplexListNode{
int value;
ComplexListNode* m_pNext;
ComplexListNode* m_pSibling;
}
</code></pre>

<pre><code> public static ComplexListNode cloneNode(ComplexListNode refrence) {
cloneNodes(refrence);
connectSiblingNode(refrence);
return reconnectNodes(refrence);
}

private static ComplexListNode reconnectNodes(ComplexListNode refrence) {
    ComplexListNode clonedHead = null;
    ComplexListNode clonedNode = null;
    if (refrence != null) {
        clonedHead = clonedNode = refrence.next;
        refrence.next = clonedHead.next;
        refrence = refrence.next;
    }
    while (refrence != null) {
        clonedNode.next = refrence.next;
        clonedNode = clonedNode.next;
        refrence.next = clonedNode.next;
        refrence = refrence.next;
    }
    return clonedHead;
}

private static void connectSiblingNode(ComplexListNode refrence) {
    while (refrence != null) {
        ComplexListNode cloned = refrence.next;
        if (refrence.sibling != null) {
            cloned.sibling = refrence.sibling.next;
        }
        refrence = cloned.next;
    }
}

private static void cloneNodes(ComplexListNode refrence) {
    if (refrence == null)
        throw new NullPointerException("invaild paramer");
    while (refrence != null) {
        ComplexListNode cloned = new ComplexListNode(refrence.value);
        cloned.next = refrence.next;
        cloned.sibling = null;
        refrence.next = cloned;
        refrence = cloned.next;
    }
}

</code></pre>

<h4>三次遍历</h4>

<blockquote>
思路:先遍历一便,在每个node后复制自身,然后,再次遍历,让node的clone指向node的随机node的下一个,再次遍历,连接所有node的clone,返回nodeHead。
</blockquote>

<h3>面试题二十七:二叉搜索树与双向链表(懵)</h3>

<blockquote>
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的节点,只能调整数中节点指针的指向。
</blockquote>

<h4>思路</h4>

中序遍历+将真正的头节点和当前头节点存储在一起。

<pre><code> public class Solution {
TreeNode head = null;
TreeNode realHead = null;

    public TreeNode convert(TreeNode root) {
        convertSub(root);
        return realHead;
    }

    public void convertSub(TreeNode node) {
        if (node == null)
            return;
        convertSub(node);
        if (head == null) {
            head = node;
            realHead = node;
        } else {
            head.right = node;
            node.left = head;
            head = node;
        }
        convertSub(node.right);
    }
}

</code></pre>

<h3>面试题二十八:字符串的排列</h3>

<blockquote>
题目:输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出a、b、c所能排列出来的所有字符串。
</blockquote>

<pre><code>package offer;

/**

}

</code></pre>

<h3>面试题二十九:数组中出现次数超过一半的数字</h3>

<blockquote>
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
</blockquote>

<pre><code><br />static int moreThanHalfNum(int[] numbers) {
int result = numbers[0];
int times = 1;
for (int i = 1; i < numbers.length; i++) {
if (times == 0) {
result = numbers[i];
times = 1;
} else if (numbers[i] == result) {
times++;
} else {
times--;
}
}
if (checkMoreThanHalf(numbers, result))
return result;
else
return 0;
}

private static boolean checkMoreThanHalf(int[] numbers, int result) {
    int times = 0;
    for (int i = 0; i &lt; numbers.length; i++) {
        if (numbers[i] == result)
            times++;
    }
    boolean isMoreThanHalf = true;
    if (times * 2 &lt;= numbers.length) {
        isMoreThanHalf = false;
    }
    return isMoreThanHalf;
}

</code></pre>

<h4>思路</h4>

两种方式:

  1. 快速排序,取中间值校验。
  2. 根据数组的特点,选出出现次数最多的,然后校验。

<h3>面试题三十:最小的k个数</h3>

<blockquote>
题目:输入n个整数,找出其中最小的k个数,例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
</blockquote>

<h4>思路</h4>

<ol>
<li>基于数组左边的第K个数字进行快速排序,使得所有比第k个数字小的所有数字都位于数组的左边</li>
<li>使用一个容器,装填最小的k个数字,满了以后从其中找出最大数,删除。</li>
</ol>

<pre><code>/**

}

</code></pre>

<h3>面试题三十一:连续子数组的最大和</h3>

<blockquote>
题目:输入一个整形数组,数组里有正数也有负数。数组中一个活连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(1)
</blockquote>

<h4>思路:</h4>

如果之前的子数组之和小于0,那就直接从新的数字开始做子数组,每次更改后,就比较,取大值。

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

</code></pre>

<h3>面试题三十二:从1到n整数中1出现的次数</h3>

<blockquote>
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数,例如输入12,从1到12这些整数中包含1的数字有1,10,11和12,1一共出现了5次
</blockquote>

<h4>思路:</h4>

<ol>
<li>不考虑时间复杂度的情况下,遍历每个数字,得到每个数字中1的个数,然后累加。</li>
<li>每次去掉最高位然后做递归,递归的次数和位数相同。一个数字n有O(logn)位,因此这种思路的时间复杂度是O(logn),比前面的原始方法药好很多。</li>
</ol>

<pre><code class="java"> public static class Solution32II {
public int NumberOf1Between1AndN_Solution(int num) {
if (num < 10)
return 1;
int firstPosition = num;
int length = 0;
//求得首位数字
while (firstPosition > 10) {
firstPosition = firstPosition / 10;
length++;
}
//求得剩余数字
int other = num - firstPosition * powerBase10(length);

        int numFirstDight = 0;
        if (firstPosition &gt; 1)
            numFirstDight = powerBase10(length);
        else if (firstPosition == 1)
            numFirstDight = other + 1;
        int numberOther = firstPosition * (length) * powerBase10(length - 1);
        return numFirstDight + numberOther + NumberOf1Between1AndN_Solution(other);
    }

    private int powerBase10(int n) {
        int result = 1;
        for (int i = 0; i &lt; n; ++i)
            result *= 10;
        return result;
    }
}

</code></pre>

<h3>面试题三十三:把数组排成最小的数</h3>

<blockquote>
题目:输入一个正整数数组,把数组里的所有数字拼接起来排成一个数,打印出能拼接的所有数字中最小的一个。例如输入数组{3,32,321},打印出最小的数字是{321323};
</blockquote>

<h4>思路:</h4>

使用快速排序,更改判定条件即可

<pre><code class="java">package offer;

/**

public class Offer33 {
public static void main(String... args) {
int data[] = {321, 23, 1111};
Utils.syso(new Solution33().getNum(data));
}

public static class Solution33 {

    public String getNum(int[] data) {
        sort(data, 0, data.length - 1);
        StringBuilder sb = new StringBuilder();
        for (int i : data)
            sb.append(i);
        return sb.toString();
    }

    private void sort(int[] data, int left, int right) {
        if (left &lt; right) {
            int position = partition(data, left, right);
            if (position == left)
                sort(data, left + 1, right);
            if (position == right)
                sort(data, left, right - 1);
            else {
                sort(data, left, position - 1);
                sort(data, position + 1, right);
            }
        }
    }

    private int partition(int[] data, int left, int right) {
        int value = data[right];
        int n = left;
        for (int i = left; i &lt; right; i++) {
            if (isSmall(data[i], value)) {
                changeE(data, i, n);
                n++;
            }
        }
        changeE(data, n, right);
        return n;
    }

    public boolean isSmall(int m, int n) {
        String left = String.valueOf(m) + String.valueOf(n);
        String right = String.valueOf(n) + String.valueOf(m);
        for (int i = 0; i &lt; left.length(); i++) {
            if (left.charAt(i) &lt; right.charAt(i))
                return true;
            else if (left.charAt(i) &gt; right.charAt(i))
                return false;
        }
        return false;
    }

    private void changeE(int[] data, int i, int n) {
        if (i == n)
            return;
        data[i] = data[i] + data[n];
        data[n] = data[i] - data[n];
        data[i] = data[i] - data[n];
    }
}

}

</code></pre>

<h3>面试题三十四:丑数</h3>

<blockquote>
题目:我们把只包含印子2、3、5的数称为丑数。求按从小到大的顺序的第1500个丑数,例如6,8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当作第一个丑数。
</blockquote>

<pre><code class="java">package offer;

/**

}

</code></pre>

<h3>面试题35:第一次只出现一次的字符。</h3>

<blockquote>
题目:在字符串中找出第一个只出现一次的字符。如输入“abaccdeff”,则输出‘b'。
</blockquote>

<pre><code class="java">package offer;

import java.util.HashMap;

/**

</code></pre>

思路:

使用hashmap,遍历两次即可。

<h3>面试题三十六:数组中的逆序对</h3>

<blockquote>
题目:在数组中的两个数字如果前面一个大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
</blockquote>

<pre><code class="java">package offer;

/**

}

</code></pre>

<h3>面试题三十七:两个链表的第一个公共节点。</h3>

<blockquote>
题目:输入两个链表,找出它们的第一个公共节点。
</blockquote>

<h4>思路:</h4>

先得出两个链表的长度差,然后,先在长链表中走k步,然后同时走,直到node相同。

<h3>面试题三十八:</h3>

<blockquote>
题目:统计一个数字在排序数组中出现的次数,例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4
</blockquote>

思路:使用二分查找先找出最左边出现的k的下标,再找出最右边出现的k的下标。相减即可得到次数。

<pre><code>package offer;

/**

}

</code></pre>

<h3>面试题三十九:二叉树的深度</h3>

<blockquote>
题目一:输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点形成的树的一条路径,最长路径的长度为树的深度。
</blockquote>

<pre><code> private static int getHeight(TreeNode firstNode) {
if (firstNode == null)
return 0;
int left = getHeight(firstNode.left);
int right = getHeight(firstNode.right);
return left > right ? left + 1 : right + 1;
}
</code></pre>

<h4>思路:通过递归,取左右子树的较大值加1。</h4>

<blockquote>
题目二:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一颗平衡二叉树。
</blockquote>

<pre><code> static boolean isBalanced(TreeNode node, int[] depth) {
if (node == null) {
depth[0] = 0;
return true;
}
int[] left = new int[1], right = new int[1];
if (isBalanced(node.left, left) && isBalanced(node.right, right)) {
int diff = left[0] - right[0];
if (diff <= 1 && diff >= -1) {
depth[0] = 1 + (left[0] > right[0] ? left[0] : right[0]);
return true;
}
}
return false;
}
</code></pre>

<h4>思路:</h4>

通过递归求出左右节点的深度差值。

上一篇 下一篇

猜你喜欢

热点阅读