[面经]CVTE的两道笔试编程题

[面经]CVTE的两道笔试编程题

晚上状态有点不好,然后就仓促参加笔试了。前面很多道选择题,真是坑,都是多选(混杂了多道单选)。下面就说说笔试题的两道编程题吧。其实我做的时候也是挺紧张的,随意符合题意的做完提交,也没加以优化,其实如果时间允许,我也是蛮想优化下的,不过这 场笔试不是看你优化得有多好,而是看你做对了没。

第一道题:字符数组的循环右移问题

题目要求:将N个字符的数组,循环右移K位。时间复杂度O(N)。
现场思路:
1. k可以小于N,大于N,等于N,按照这几种请况分析。首先我们必须知道,当N=dK(d=0,1,2..)时,字符数组循环右移后,字符数组中字符位置不变。有了这个“突破口”我们就能将K>N转化成K<=N来解,这样缩小了判断的范围。
2. 接下来我们只需要分析K

public static void solution(int[] arr, int length, int shiftStep)
{
    if(arr==null||length==0) return;
    int count=shiftStep%length;
    if(count==0) return;
    int[] copy=new int[length];
    for(int i=0;i<length;i++){
        copy[i]=arr[i];
    }
    for(int i=0;i<length;i++){
        arr[(i+count)%length]=copy[i];
    }
}

第二道题:字符串去重问题

题目要求:删除小写字母字符串中重复字符。如果可以,优先删除重复字符中排在比他小字符前面的字符。 比如,输入:bbcacdww;输出:bacdw
现场思路:一开始,我本来在想这个问题的最优解,排除使用“暴破”。想了一阵子,可能是太紧张了,思路不开。时间快到了,被迫是用“暴破”。然而“暴破”也不是那么容易的,orz。

public static String solution(String s)
{
    if(s==null||"".equals(s)) return s;

    StringBuilder sb = new StringBuilder(s);
    int i = 0;
    while (i < sb.length())
    {
        char temp = sb.charAt(i);
        boolean isR = false; // 是否有重复的字符在i的前面
        boolean isFront = true; // 要删除后面的重复字符?true删除后面的重复字符:false删除前面的重复字符
        int j = i + 1;
        while (j < sb.length())
        {
            if (isFront && sb.charAt(j) < temp)
            {
                isFront = false;
            }
            if (sb.charAt(j) == temp)
            {
                if (isFront)
                {
                    sb.deleteCharAt(j);
                    continue;
                }
                else
                {
                    isR = true;
                }
            }
            j++;
        }
        if (isR)
        {
            sb.deleteCharAt(i);
        }
        else
        {
            i++;
        }
    }
    return sb.toString();
}

总结: 算法题还是要有针对性的刷题,然后从中提取解法和经验。

作者:KesarChen 发表于2016/3/5 22:49:15 原文链接
阅读:868 评论:0 查看评论
 
[算法学习]8皇后问题

问题描述: 国际象棋的“8皇后问题”:在8x8的国际象棋上摆放8个皇后,使其不能互相攻击,即8个皇后中任意两个不得处在同一行、同一列或同一对角线上。共有多少种符合条件的摆法?
解法与分析:
1. 首先是不能同一列同一行,那么每个皇后必须各独占一行一列,使用二维数组来描述就有点浪费空间了。这里我们使用一个8个元素的一维数组来表示每个皇后的每一行。
2. 那么独占一列的情况,描述起来就像是实现{0,1,2,3,4,5,6,7}这个一维数组的全排列。
3. 最后要解决的是不能在同一对角线的判断:假设两个数载一维数组nums位置i,j,那么i,j在同一对角线的条件是 i-j = nums[i]-nums[j] || j-i = nums[i]-nums[j]。


参考代码如下

/**
 * @Description: 打印所有"8皇后"的可能位置
 * @param
 * @return void
 */
public static void eightQueen()
{
    int[] queens = { 0, 1, 2, 3, 4, 5, 6, 7};
    getAllArrange(queens, 0, queens.length - 1);
}

/**
 * @Description: 数组全排列筛选
 * @param @param nums
 * @return void
 */
private static void getAllArrange(int[] nums, int start, int end)
{
    if (start > end)
    {
        System.out.println(Arrays.toString(nums));
        return;
    }
    boolean isContinue = false;
    for (int i = start; i <= end; isContinue=false,i++)
    {
        exch(nums, start, i);
        for (int a = start - 1; a >= 0; a--)
        {
            if (isDiagonal(nums, a, start))
            {
                exch(nums, start, i);
                isContinue=true;
                break;
            }
        }
        if(isContinue)
        {
            continue;
        }
        getAllArrange(nums, start + 1, end);
        exch(nums, start, i);
    }
}

/**
 *
 * @Description: 判断两个位置是否在同一对角线上
 * @param @param nums
 * @param @param i
 * @param @param j
 * @param @return
 * @return boolean
 */
private static boolean isDiagonal(int[] nums, int i, int j)
{
    int result = nums[i] - nums[j];
    return (i - j) == result || (j - i) == result;
}

/**
 *
 * @Description: 交换数组中的两个数的值
 * @param @param nums
 * @param @param i
 * @param @param j
 * @return void
 */
private static void exch(int[] nums, int i, int j)
{
    if (i == j)
        return;
    nums[i] ^= nums[j];
    nums[j] ^= nums[i];
    nums[i] ^= nums[j];
}
作者:KesarChen 发表于2016/2/19 15:21:16 原文链接
阅读:5021 评论:0 查看评论
 
[算法学习]Java实现字符序列全组合

问题描述: 假设数组中字符无重复,输入一个字符数组,打印出字符的全部组合。例如:输入{a,b,c}输出a、b、c、ab、ac、bc、abc

解法与分析: 不使用辅助空间。输入一组n个字符的数组,将打印出C(1,n)+…+C(n,n)=2^n个组合。
1.按照从1到n作为一个循环,每次输出C(i,n)个组合。这样,我们只需要处理如何输出一个C(i,n)就可以解决问题。
2.处理C(i,n):i有两种特殊情况,一种是i=1,那么直接输出n个字符中每个字符就可以;一种是i=n,那么整个字符数组一起输出就可以。i>1&&i

参考代码如下

/**
 * 从C(1,n)计算到C(n,n),时间复杂度是C(1,n)+...+C(n,n)=O(2^n)
 * 应该有不用递归的更优解法吧,但是我一时也想不过来,只能用递归来解。
 * @param cs
 */
public static void printAllSeqCombination(char[] cs)
{
    if (cs == null || cs.length == 0)
        return;
    for (int i = 1; i <= cs.length; i++)
    {
        printAllSeqCombination(cs, 0, cs.length - 1, 0, i - 1);
    }
}

/**
 *
 * @param cs 
 * @param array_start 能被组合的数组元素开始第一个元素
 * @param array_end 能被组合的数组元素最后一个元素
 * @param print_start 要被打印出来的第一个index
 * @param print_end 要被打印出来的最后一个一个index
 */
private static void printAllSeqCombination(char[] cs, int array_start,int array_end, int print_start, int print_end)
{
    if (print_end == array_end)
    {
        System.out.println(new String(cs, print_start, print_end- print_start + 1));
        return;
    }
    if (array_start == print_end)
    {
        for (int i = array_start; i <= array_end; i++)
        {
            exch(cs, array_start, i);
            System.out.println(new String(cs, print_start, print_end- print_start + 1));
            exch(cs, array_start, i);
        }
        return;
    }
    while (print_end<=array_end)
    {
        printAllSeqCombination(cs, ++array_start, array_end, print_start++, print_end++);
    }
}
/**
 *
 * @Description: 交换数组中两个数的值
 * @param @param cs
 * @param @param i
 * @param @param j
 * @return void
 * @throws
 */
private static void exch(char[] cs, int i, int j)
{
    if (i == j)
        return;
    char temp = cs[i];
    cs[i] = cs[j];
    cs[j] = temp;
}
作者:KesarChen 发表于2016/2/18 14:00:31 原文链接
阅读:5008 评论:0 查看评论
 
[算法学习]整数序列的奇偶分类

问题描述: 输入一组序列,奇数在前,偶数在后,输出整理后的序列。

解法与分析: 能马上想到用二分法,就很快能解决问题。


参考代码如下

/**
 * 解法:使用二分法
 * @param nums
 */
public static void reorderOddEven(int[] nums)
{
    if(nums==null)
    {
        return;
    }
    int left=0;
    int right=nums.length-1;
    while (left<right)
    {
        while (left<right&&nums[left]%2==1)
        {
            left++;
        }
        while (left<right&&nums[right]%2==0)
        {
            right--;
        }
        if(left>=right)
        {
            break;
        }
        exch(nums, left, right);
    }
}

/**
 *
 * @Description: 交换数组中两个数的值
 * @param @param nums
 * @param @param index1
 * @param @param index2
 * @return void
 */
private static void exch(int[] nums,int index1,int index2)
{
    if(index1==index2)
    {
        return;
    }
    nums[index1]^=nums[index2];
    nums[index2]^=nums[index1];
    nums[index1]^=nums[index2];
}
作者:KesarChen 发表于2016/2/18 13:59:51 原文链接
阅读:4973 评论:0 查看评论
 
[算法学习]顺时针打印矩阵

问题描述: 顺时针打印矩阵
例如:
{
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16}
}
打印结果:1、2、3、4、8、12、16、15、14、13、9、5、6、7、11、10

解法与分析: 考虑几个特殊情况:行1列n,行n列1,行1列1。考虑到这几个后其实就不难了。


参考代码如下

/**
 * 顺时针打印矩阵
 *
 * @param matrix
 */
public static void clockwisePrintMatrix(int[][] matrix)
{
    // 1.test代码
    if (matrix == null)
    {
        System.err.println("输入矩阵有误");
        return;
    }
    int hight = matrix.length;
    if (hight == 0)
    {
        System.err.println("输入矩阵有误");
        return;
    }
    int width = matrix[0].length;
    for (int i = 1; i < hight; i++)
    {
        if (matrix[i].length != width)
        {
            System.err.println("输入矩阵有误");
            return;
        }
    }
    if (width == 0)
    {
        System.err.println("输入矩阵有误");
        return;
    }

    // 2.核心代码
    int loopNum = width > hight ? (hight + 1) / 2 : (width + 1) / 2;

    printMatrix(matrix, 0, 0, width, hight);
    for (int i = 1; i < loopNum; i++)
    {
        printMatrix(matrix, i, i, width - 2, hight - 2);
    }
    System.out.println();
}

/**
 * 顺时针打印一圈矩阵
 *
 * @param matrix
 * @param x
 * @param y
 * @param xLen
 * @param yLen
 */
private static void printMatrix(int[][] matrix, int x, int y, int xLen, int yLen)
{
    for (int i = 0; i < xLen; i++)
    {
        System.out.print(matrix[y][x + i] + ",");
    }
    for (int i = 1; i < yLen; i++)
    {
        System.out.print(matrix[y + i][x + xLen-1] + ",");
    }
    for (int i = 2; i <=xLen&&yLen!=1; i++)
    {
        System.out.print(matrix[y + yLen-1][x + xLen - i] + ",");
    }
    for (int i = 2; i <yLen&&xLen!=1; i++)
    {
        System.out.print(matrix[y + yLen - i][x] + ",");
    }
}
作者:KesarChen 发表于2016/2/18 13:59:02 原文链接
阅读:4734 评论:0 查看评论
 
[算法学习]数组的旋转

问题描述: 返回将一维数组向右旋转k个位置的结果。
比如,一维数组{1,2,3,4,5},k=2时,返回结果是{4,5,1,2,3}。 要求常数级空间复杂度,允许修改原有数组。

解法与分析: 使用三旋转:全部旋转一次,前面0到k-1旋转一次,后面k到数组最后一个数旋转一次,就可以解决问题。


参考代码如下

public static int[] rotateK(int[] datas,int k)
{
    reverse(datas, 0, datas.length-1);
    reverse(datas, 0, k-1);
    reverse(datas,k,datas.length-1);
    return datas;
}

/**
 * 反转start和end之间的数
 * @param datas
 * @param start
 * @param end
 */
public static void reverse(int[] datas,int start,int end)
{
    while(start<end)
    {
        // 交换两个数的值
        datas[start]^=datas[end];
        datas[end]^=datas[start];
        datas[start]^=datas[end];
        start++;
        end--;
    }
}
作者:KesarChen 发表于2016/2/18 13:58:13 原文链接
阅读:5039 评论:0 查看评论
 
[算法学习]二维数组的查找

问题描述: 二维数组中的查找。在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样一个二维数组和一个整数,判断数组中是否包含该整数。
例如:
{
{1,2,3,4}
{2,4,6,7}
{4,5,7,8}
{6,7,8,9}
}

解法与分析: 可以使用二分法来查找。
1. 从四个角中找一个起点,向列走是递增&向行走是递减,或者,向列走是递减&向行走是递增。符合这个条件的只有左下角和右上角。
2. 以左下角为例,定义两个指针,一个向上走(减),一个向右走(增)。
3. 如何走?判断当前两个指针所指向二维数组中的值比搜索值大还是小,比搜索值大的话,向上走,比搜索值小的话,向右走。直到两个指针都停止不能走为止。


参考代码如下

/**
 * 查找。我们从二维数组左下角找起会很快。(以下计算过程画个二维数组图,然后一行一行代码看会很容易理解的)
 * @param nums 二维数组
 * @param target 目标整数
 * @param column 取小列
 * @param row 取最大行
 * @return
 */
public static boolean findNum(int[][] nums,int target,int column,int row)
{
    if(nums==null)
    {// 防止 null异常
        return false;
    }
    while (column>0&&row>0)
    {
        int current=nums[row-1][column-1]; //当前值

        if(current>target)
        {// 当前值太大,缩行(向上面一行走,因为上面一行值会小一点)
            --row;
        }
        else if(current<target)
        {// 当前值太小,缩列(向右边一列走,因为右边一列值会大一点)
            ++column;
        }
        else
        {// 找到值
            return true;
        }
    }

    return false;
}
作者:KesarChen 发表于2016/2/18 13:57:28 原文链接
阅读:5266 评论:0 查看评论
 
[算法学习]复杂链表的复制

问题描述: 复制一个复杂链表。 在复杂链表中,每个结点除了有一个指向下一个结点的next指针外,还有一个 sibing指针 指向链表中任意结点或者NULL。

解法一:“爆破”

解法分析:
1. 遍历第一遍数组,把复制链的next和各结点值复制好。需要时间O(n)
2. 第二遍遍历,二重for循环根据原链表,将复制链的slibing指针连接好。需要时间O(n^2)
3. 需要时间复杂度是O(n^2),空间复杂度是O(1)


参考代码如下

/**
 * 复杂链表的结点
 *
 * @author kesar
 *
 */
static class Node
{
    int val;
    Node next;
    Node slibing;

    public Node(){}

    public Node(int val)
    {
        this.val = val;
    }

    @Override
    public String toString()
    {
        return "Node [val=" + val + "]";
    }
}

/**
 * 第一种解法:暴力复制。复制一遍next的,再每个结点遍历一遍进行slibing的比较。 时间复杂度是:O(n^2),空间复杂度是:O(1)
 *
 * @param oHead
 * @return
 */
public static Node copy1(Node oHead)
{
    if (oHead == null)
    {
        return null;
    }
    // 复制的链表的头结点
    Node cHead = new Node(oHead.val);

    // oMove是原链表上遍历用的移动指针,cMove是复制链表上遍历用的移动指针
    // 复制Node结点的值以及next链关系
    for (Node oMove = oHead.next, cMove = cHead; oMove != null; cMove = cMove.next, oMove = oMove.next)
    {
        cMove.next = new Node(oMove.val);
    }

    // 复制slibing的链关系
    for (Node oMove = oHead, cMove = cHead; oMove != null; cMove = cMove.next, oMove = oMove.next)
    {
        if (oMove.slibing == null)
            continue;
        // 查找开始: 遍历查找slibing所指原链表结点对应在copy链表中的结点
        Node findNode = cHead;
        for (; findNode != null && findNode.val != oMove.slibing.val; findNode = findNode.next)
            ;
        // 查找结束:findNode就是结果
        cMove.slibing = findNode;
    }

    return cHead;
}

解法二:使用辅助空间

解法分析:
1. 使用一个HashMap(Key:原链表上的结点,Value:复制链表上的结点),空间复杂度是O(n)
2. 遍历第一遍数组:每次创建复制链的结点时,需要先查找HashMap中结点是否存在,不存在才创建,然后保存结点到HashMap中。需要时间O(n)
4. 需要时间复杂度是O(n),空间复杂度是O(n)


参考代码如下

/**
 * 第二种解法:利用HashMap的O(1)的高效率查找来取代第一中解法中的一个for循环。用O(n)的空间换来O(n)的时间。时间复杂度O(n),空间复杂度:O(n)
 *
 * @param oHead
 * @return
 */
public static Node copy2(Node oHead)
{
    if (oHead == null)
    {
        return null;
    }
    // 辅助空间:Key:原链表上的结点,Value:复制链表上的结点
    HashMap<Node, Node> map = new HashMap<Node, Node>();

    // 初始化复制链表的头结点
    Node cHead = new Node(oHead.val);
    map.put(oHead, cHead);
    if (oHead.slibing != null)
    {
        cHead.slibing = new Node(oHead.slibing.val);
        map.put(oHead.slibing, cHead.slibing);
    }
    // 作用:遍历原链表,复制结点到复制链表中。
    // 思路是这样:先从map找原结点对应的复制结点,如果没找到,就创建一个,然后连接到复制链表中,然后put入map中,便于其他时候使用
    for (Node oMove = oHead.next, cMove = cHead; oMove != null; oMove = oMove.next, cMove = cMove.next)
    {
        if (map.containsKey(oMove))
        {
            cMove.next = map.get(oMove);
        }
        else
        {
            cMove.next = new Node(oMove.val);
            map.put(oMove, cMove.next);
        }
        Node slibing = oMove.slibing;
        if (slibing == null)
        {
            continue;
        }
        if (map.containsKey(slibing))
        {
            cMove.next.slibing = map.get(slibing);
        }
        else
        {
            cMove.next.slibing = new Node(slibing.val);
            map.put(slibing, cMove.next.slibing);
        }
    }
    return cHead;
}

解法三:“伸长分裂法”

解法分析:
1. 遍历原链表。在原链表的每个结点的后面插入对应复制结点,将原链表“伸长”1倍;
2. 遍历原链表。将复制结点的slibing连接好;
3. 遍历原链表。将原结点和复制结点“分裂”开,将next连接好;
4. 需要时间复杂度是O(n),空间复杂度是O(1)


参考代码如下

/**
 * @param oHead
 * @return
 */
public static Node copy3(Node oHead)
{
    if (oHead == null)
    {
        return null;
    }
    // 1."伸长"
    for (Node oMove = oHead; oMove != null; oMove = oMove.next)
    {
        // 复制结点
        Node copy = new Node(oMove.val);
    // 插入复制结点到原结点的后面
        copy.next = oMove.next;
        oMove.next = copy;
        // 移动到复制结点处
        oMove = copy;
    }
    // 2.连接好slibing
    for (Node oMove = oHead; oMove != null; oMove = oMove.next)
    {
        if (oMove.slibing == null)
        {
            continue;
        }
        Node copy = oMove.next;
        copy.slibing = oMove.slibing.next;
        oMove = copy;
    }
    // 3."分裂",连接好原链表的next和复制链表中的next
    Node cHead = oHead.next;
    oHead.next = cHead.next;
    cHead.next = null;
    for (Node oMove = oHead.next, cMove = cHead; oMove != null; oMove = oMove.next, cMove = cMove.next)
    {
        Node copy = oMove.next;
        oMove.next = copy.next;
        copy.next = null;
        cMove.next = copy;
    }
    return cHead;
}
作者:KesarChen 发表于2016/2/18 13:56:50 原文链接
阅读:4892 评论:0 查看评论
 
[算法学习]求链表的中间结点

问题描述: 返回链表的中间结点(如果是两个则返回两个)

解法与分析: 用两个指针移动的方法来解决问题。一个指针每次移动1个结点,另外一个指针移动2个结点。


参考代码如下

static class ListNode
{
    int val;
    ListNode next;

    public ListNode(int val)
    {
        this.val = val;
    }

    public ListNode(int val, ListNode next)
    {
        this.val = val;
        this.next = next;
    }

    @Override
    public String toString()
    {
        return "ListNode [val=" + val + "]";
    }
}

/**
 * 返回中间节点
 * @param head
 * @return
 */
public static ListNode[] getCenterNode(ListNode head)
{
    ListNode[] results = { null, null };
    if (head == null)
    {
        return results;
    }
    ListNode left = head;
    ListNode right = head.next;
    // 如果只有一个节点
    if (right == null)
    {
        results[0] = left;
        return results;
    }
    // 如果不止有一个节点
    while (right.next != null)
    {
        left = left.next;
        right = right.next;
        right = right.next;
        if (right == null)
        {
            results[0] = left;
            return results;
        }
    }
    results[0] = left;
    results[1] = left.next;
    return results;
}
作者:KesarChen 发表于2016/2/18 13:56:00 原文链接
阅读:4397 评论:0 查看评论
 
[算法学习]求出链表中倒数第n个节点

问题描述: 求出链表中倒数第n个节点。例如:1,2,3,4,5,倒数第2个是4

解法与分析: 用两个指针的方法。
1. 第一个指针起始位置在0位置,第二个指针起始位置在n-1位置。
2. 两个指针同时移动,每次移动1个结点。
3. 当第二个指针移动到最后一个节点时,第一个指针所指结点就是倒数第n个结点。


参考代码如下

static class ListNode
{
    int val;
    ListNode next;

    public ListNode(int val)
    {
        this.val = val;
    }

    public ListNode(int val, ListNode next)
    {
        this.val = val;
        this.next = next;
    }

    @Override
    public String toString()
    {
        return "ListNode [val=" + val + "]";
    }
}

public static ListNode getLastNode(ListNode head, int n)
{
    if (head == null || n <= 0)
    {
        return null;
    }

    ListNode left = head;
    ListNode right = head;

    for (int i = 1; i < n; i++)
    {
        right = right.next;
        if (right == null)
        {
            return null;
        }
    }
    while (right.next != null)
    {
        left = left.next;
        right = right.next;
    }
    return left;
}
作者:KesarChen 发表于2016/2/18 13:55:19 原文链接
阅读:5212 评论:0 查看评论
 
[算法学习]判断链表是否是环结构

问题描述: 判断链表是否是环结构。

解法与分析: 用两个指针的方法判断。
1. 第一个指针在第一个结点,第二个指针在第二个结点处;
2. 两个指针同时移动,第一个指针每次移动一个结点,第二个指针每次移动两个结点;
3. 当第二个指针和第一个指针指向的结点是同一个时,链表有环。


参考代码如下

static class ListNode
{
    int val;
    ListNode next;

    public ListNode(int val)
    {
        this.val = val;
    }

    public ListNode(int val, ListNode next)
    {
        this.val = val;
        this.next = next;
    }

    @Override
    public String toString()
    {
        return "ListNode [val=" + val + "]";
    }
}

/**
 * 判断链表是否是环结构
 * @param head
 * @return
 */
public static boolean isLoop(ListNode head)
{
    if(head==null||head.next==null)
    {
        return false;
    }
    ListNode pSlow=head;
    ListNode pFast=head.next;

    while (pFast.next!=null)
    {
        if(pFast.equals(pSlow))
        {
            return true;
        }
        pSlow=pSlow.next;
        pFast=pFast.next;
        pFast=pFast.next;
        if(pFast==null)
        {
            return false;
        }
    }
    return false;
}
作者:KesarChen 发表于2016/2/18 13:54:34 原文链接
阅读:5303 评论:0 查看评论
 
[算法学习]合并两个排序的链表

问题描述: 合并两个排序的链表。

解法一:用递归

解法: 递归比较结点大小:每次递归,取出两个链表的头结点来比较,比较小的结点加入新链表中。


参考代码如下

/**
 * 用递归
 * @param head1
 * @param head2
 * @return
 */
public static ListNode merge(ListNode head1, ListNode head2)
{
    if(head1==null)
    {
        return head2;
    }
    if(head2==null)
    {
        return head1;
    }
    ListNode head;
    if(head1.value<head2.value)
    {
        head=head1;
        head.next=merge(head1.next, head2);
    }
    else
    {
        head=head2;
        head.next=merge(head1, head2.next);
    }
    return head;
}

解法二:用循环

解法: 使用指针移动的方式来遍历两个链表。每次两个指针停止,进行结点比较,较小的结点加到新链表中。


参考代码如下

/**
 * 用循环
 * @param head1
 * @param head2
 * @return
 */
public static ListNode merge1(ListNode head1, ListNode head2)
{
    if (head1 == null)
    {
        return head2;
    }
    if (head2 == null)
    {
        return head1;
    }
    ListNode head;
    if (head1.value < head2.value)
    {
        head = head1;
        head1 = head1.next;
    }
    else
    {
        head = head2;
        head2 = head2.next;
    }
    ListNode move = head;
    while (head1 != null || head2 != null)
    {
        if (head1 == null)
        {
            move.next = head2;
            head2 = head2.next;
        }
        else if (head2 == null)
        {
            move.next = head1;
            head1 = head1.next;
        }
        else if (head1.value < head2.value)
        {
            move.next = head1;
            head1 = head1.next;
        }
        else
        {
            move.next = head2;
            head2 = head2.next;
        }
        if(move.value>move.next.value)
        {
            System.err.println("输入的链表不是有序的");
            return null;
        }
        move = move.next;
    }
    return head;
}
作者:KesarChen 发表于2016/2/18 13:53:35 原文链接
阅读:5001 评论:0 查看评论
 
[算法学习]反转链表

问题描述: 输入一条链表,将链表反转后返回。

解法一:用递归

解法: 由于反转后头结点会成为尾结点,尾结点会成会头结点。所以从头结点开始反转前后结点关系。比较简单,所以这里直接贴出代码。


参考代码如下

/**
 * 递归
 * @param head
 * @return
 */
public static ListNode reverseList1(ListNode head)
{
    if(head==null)
    {
        return null;
    }
    ListNode parent=head.next;
    if(parent==null)
    {
        return head;
    }
    head.next=null;
    return reverseList1(head,parent);
}

/**
 * 递归,绑定父子节点关系
 * @param child
 * @param parent
 * @return
 */
private static ListNode reverseList1(ListNode child,ListNode parent)
{
    if(parent==null)
    {
        return child;
    }
    ListNode last=parent.next;
    parent.next=child;
    return reverseList1(parent, last);
}

解法二:用循环,效率更高


参考代码如下

/**
 * 反转链表,返回新的头节点
 * @param head
 * @return
 */
public static ListNode reverseList(ListNode head)
{
    if(head==null)
    {
        return null;
    }
    ListNode pChild=head;
    ListNode pParent=pChild.next;
    ListNode pLast=null;
    pChild.next=null;
    while (pParent!=null)
    {
        // 绑定关系
        pLast=pParent.next;
        pParent.next=pChild;
        // 设定关系
        pChild=pParent;
        pParent=pLast;
    }
    return pChild;
}
作者:KesarChen 发表于2016/2/18 13:52:54 原文链接
阅读:5088 评论:0 查看评论
 
[算法学习]计算出二进制数中1的个数

问题描述: 计算出二进制数中1的个数。

解法一:循环计数

解法与分析: 使用一个数1来“与”二进制数中每一位,若值为1则1的个数加一。


参考代码如下

/**
 * 通过flag左移做一个循环来与num的每一位,若与计算的结果不为0,则1的个数++
 * @param num
 * @return
 */
public static int calc2(int num)
{
    int count=0;
    int flag=1;
    while (flag!=0)
    {
        if((num&flag)!=0)
        {
            count++;
        }
        flag<<=1;
    }

    return count;
}

解法二:最佳算法

解法与分析: 设二进制数为num,若num不为0时,num-1后的值与num相与都会消除一个1,按照这一点,可以很快计算出1个个数。


参考代码如下

/**
 * num不为0时,num-1后的值与num相与都会消除一个1,按照这一点,可以很快计算出1个个数,最佳算法
 * @param num
 * @return
 */
public static int calc3(int num)
{
    int count=0;
    while (num!=0)
    {
        num=num&(num-1);
        count++;
    }
    return count;
}
作者:KesarChen 发表于2016/2/18 13:52:00 原文链接
阅读:5165 评论:0 查看评论
 
[算法学习]栈中的最小值

问题描述: 实现栈的pop、push、 min (得到栈中最小值)方法。

解法与分析:
1. 由于每次压栈和出栈都可能会改变栈中的最小值,所以,我们增加一个存放最小值的栈。
2. 当出栈操作时,最小值的栈也出栈;(当然,必须判断栈是否为空)
3. 当压栈操作时,比较压栈元素值和最小栈的栈顶元素的大小,若比较小,则将它压入栈中,若不是,则将最小栈的栈顶元素重复压栈。(当然,必须判断栈是否为空)


参考代码如下

    private Stack<Integer> dataStack = new Stack<Integer>();
    private Stack<Integer> minStack = new Stack<Integer>();

    /**
     * 出栈
     * @return
     */
    public int pop()
    {
        if (minStack.isEmpty() && dataStack.isEmpty())
        {
            throw new EmptyStackException();
        }
        minStack.pop();
        return dataStack.pop();
    }

    /**
     * 压栈
     * @param item
     */
    public void push(int item)
    {
        dataStack.push(item);
        if(minStack.isEmpty()||item<minStack.peek())
        {
            minStack.push(item);
        }
        else
        {
            minStack.push(minStack.peek());
        }
    }

    /**
     * 取最小值
     * @return
     */
    public int min()
    {
        if(minStack.isEmpty())
        {
            throw new EmptyStackException();
        }
        return minStack.peek();
    }
作者:KesarChen 发表于2016/2/18 13:51:19 原文链接
阅读:4910 评论:0 查看评论
 
[算法学习]栈的出栈序列

问题描述: 输入数字不重复的两组整数序列:压栈的序列A,出栈的序列B,验证AB是否为同一栈的序列。

解法与分析:
1. 使用两个指针pA,pB分别在两个序列A,B上遍历。
2. 做个这样的判断,首先判断两个指针所指元素是否相等,若相等,则两个指针同时推进;若不相等,取出栈顶元素作比较;若与栈顶元素不相等,则序列A的元素入栈,pA指针向前推进,若与栈顶元素相等,则出栈,pB指针推进。


参考代码如下

public static boolean isStackSeq(int[] pushSeq, int[] popSeq)
{
    if (popSeq == null || pushSeq == null
            || pushSeq.length != popSeq.length)
    {
        return false;
    }
    Stack<Integer> stack = new Stack<Integer>();
    int pushIndex = 0;
    int popIndex = 0;

    while (popIndex < popSeq.length)
    {
        if (pushIndex < pushSeq.length)
        {
            if (pushSeq[pushIndex] == popSeq[popIndex])
            {
                pushIndex++;
                popIndex++;
                continue;
            }
            if (!stack.isEmpty() && stack.peek() == popSeq[popIndex])
            {
                stack.pop();
                popIndex++;
            }
            else
            {
                stack.push(pushSeq[pushIndex++]);
            }
        }
        else if (!stack.isEmpty() && stack.pop() == popSeq[popIndex])
        {
            popIndex++;
        }
        else
        {
            return false;
        }
    }
    return stack.isEmpty();
}
作者:KesarChen 发表于2016/2/18 13:50:39 原文链接
阅读:5281 评论:0 查看评论
 
[算法学习]二叉搜索树转双向链表

问题描述: 将一棵二叉搜索树转成一条排序好的双向链表。要求不能创建新结点。

解法与分析:
1. 由于左子树<根结点<右子树,所以双向链表的头结点是二叉搜索树左子树中的最后一个左结点,双向链表的头结点是二叉搜索树右子树中的最后一个右结点,根结点则是链表的中间结点。
2. 将树结点中指向左孩子的指针作为双向链表中的前驱指针,指向右孩子的指针作为双向链表指向后继结点的指针。
3. 按照上面分析,可以使用树的中序遍历来转换双向链表。这里我们使用递归的方式来实现中序遍历+双向链表转换。


参考代码如下

/**
 * 树节点
 */
static class TreeNode
{
    int val;
    TreeNode left;
    TreeNode right;

    public TreeNode(int val)
    {
        this.val = val;
    }

    public TreeNode(int val, TreeNode left, TreeNode right)
    {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
/**
 * 解法:中序遍历的顺序,中间增加连接节点的操作。
 * @param root
 * @return
 */
public static TreeNode doBST2BWL(TreeNode root)
{
    if(root==null)
    {
        return null;
    }
    TreeNode head=root;
    // 整理左半边
    TreeNode left = doBST2BWL(root.left);
    // 连接左半边
    if(left!=null)
    {
        head=left;
        // 将left指针移动到左半边的最后(右)一个结点
        for(;left.right!=null;left=left.right);
        left.right=root;
    }
    root.left=left;
    // 整理右半边
    TreeNode right= doBST2BWL(root.right);
    // 连接右半边
    if(right!=null)
    {
        right.left=root;
    }
    root.right=right;
    // 返回头结点
    return head;
}
作者:KesarChen 发表于2016/2/18 13:49:50 原文链接
阅读:5038 评论:0 查看评论
 
[算法学习]打印树的路径

问题描述: 输入一个整数sum和一棵二叉树,打印出二叉树中结点和为sum的所有路径。 (路径:从根结点往下一直到叶结点形成的一条路径。)

解法与分析:
1. 需要从树上的根结点遍历到叶子结点,其中需要累加经过结点的值。
2. 当累加到叶子结点时,比较累加结点值和sum的值,如果相等,打印出路径;如果不相等,返回到上一层,寻找其他叶子结点。
3. 需要保存结点路径,还可以返回到上一个结点,可以选择用栈来保存经过结点。
4. 可以使用递归来描述这个解法。


参考代码如下

/**
 * 树结点
 */
static class TreeNode
{
    int val;
    TreeNode left;
    TreeNode right;

    public TreeNode(int val)
    {
        this.val = val;
    }

    public TreeNode(int val, TreeNode left, TreeNode right)
    {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

/**
 * 寻找路径
 */
public static void findPath(TreeNode root, int sum)
{
    if (root == null) return;
    Stack<Integer> paths = new Stack<Integer>();
    findPath(root, paths, sum, 0);
}

/**
 * 递归寻找路径
 */
private static void findPath(TreeNode root, Stack<Integer> paths, int sum,int currentSum)
{
    currentSum += root.val;
    boolean isLeaf = root.left == null && root.right == null;
    paths.push(root.val);
    if (isLeaf && currentSum == sum)
    {
        printPath(paths);
    }
    if (root.left != null)
    {
        findPath(root.left, paths, sum, currentSum);
    }
    if (root.right != null)
    {
        findPath(root.right, paths, sum, currentSum);
    }
    paths.pop();
}

/**
 * 打印路径
 */
static void printPath(Stack<Integer> paths)
{
    if (paths.isEmpty()) return;
    for (Integer item : paths)
    {
        System.out.print(item + " - ");
    }
    System.out.println();
}
作者:KesarChen 发表于2016/2/18 13:48:59 原文链接
阅读:5086 评论:0 查看评论
 
[算法学习]树的子结构

问题描述: 判断树A是否是树B的子结构。

解法与分析: 使用递归的方式很容易解决。先进行根结点比较,相等的话,递归左右子树。


参考代码如下

public static boolean isIncludeTree(TreeNode parent, TreeNode child)
{
    boolean result = false;
    if (parent == null || child == null)
    {
        return result;
    }
    if (parent.value==child.value)
    {
        result = hasTree(parent, child);
    }
    if (!result)
    {
        result = isIncludeTree(parent.left, child);
    }
    if (!result)
    {
        result = isIncludeTree(parent.right, child);
    }

    return result;
}

private static boolean hasTree(TreeNode parent, TreeNode child)
{
    if (child == null)
    {
        return true;
    }
    if (parent == null||parent.value!=child.value)
    {
        return false;
    }

    return hasTree(parent.left, child.left) && hasTree(parent.right, child.right);
}
作者:KesarChen 发表于2016/2/18 13:47:26 原文链接
阅读:5014 评论:0 查看评论
 
[算法学习]树的镜像

问题描述: 输出树的镜像。

解法与分析: 根结点不变,左右子树交换。按照这一点进行递归,就可以解决问题。


参考代码如下

public static TreeNode mirror(TreeNode root)
{
    if(root==null) return null;

    TreeNode left=root.left;
    TreeNode right=root.right;
    root.right=mirror(left);
    root.left=mirror(right);
    return root;
}
作者:KesarChen 发表于2016/2/18 13:46:15 原文链接
阅读:5145 评论:0 查看评论
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值