算法通关村】,第一关-原来链表这么有用!---白银挑战

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 链表高频面试算法题
  • 一、两个链表的第一个公共子节点问题!
    • 1-1.哈希和集合
    • 1-2.栈
    • 2.判断链表是否为回文序列
    • 3.合并有序列表
  • 待完善todo


链表高频面试算法题

链表在算法题比数组少很多,而在回溯贪心动规等高级算法中很少见到链表的影子。我们这里就集中研究一些出现频率特别高的算法题!


1、两个链表的第一个公共子节点问题!

一、两个链表的第一个公共子节点问题!

输出两个链表,找出它们的第一个公告子节点。
算法通关村】,第一关-原来链表这么有用!---白银挑战_第1张图片
首先在这边,我需要注意的是,链表要求是环环相扣的,核心是一个节点只能有一个后继,但是不代表一个节点只能被一个指向

在没有思路的情况下,我们该如何解题呢?
以下为两种方式解析:

1-1.哈希和集合

hashmap的方法:

put(K key,V value)	增添数据
remove(Object Key)	根据键删除键值对元素
void clear()	移除所有的键值对元素
boolean containsKey(Object key)	判断集合是否包含指定的键
boolean containsValue(Object value)	判断集合是否包含指定的值
boolean isEmpty()	判断集合是否为空
V get(Object key)()	根据键获取值
Set keySet()	获取所有键集合

代码如下(示例):

 public static void main(String[] args) {
        //创建并获取初始化链表
        ListNode[] heads = initLinkedList();
        //la 为 1 2 3 4 5
        //lb 为 11 22 4 5
        ListNode la = heads[0];
        ListNode lb = heads[1];
        ListNode node = new ListNode(0);
        node = hhztFindFirstCommonNodeByMap(la, lb);
        System.out.println("map解析公共节点为:"+node.val);
        node = hhztFindFirstCommonNodeBySet(la, lb);
        System.out.println("set解析公共节点为:"+node.val);
		node = hhztFindFirstCommonNodeByStack(la, lb);
        System.out.println("栈解析公共节点为:"+node.val);
    }

    /**
     * hashMap方法解析 两个链表,找出它们的第一个公告子节点
     * @param listNode1
     * @param listNode2
     * @return
     */
    public static ListNode hhztFindFirstCommonNodeByMap(ListNode listNode1,ListNode listNode2){
        //判空
        if (listNode1==null||listNode2==null){
            return null;
        }
        Map<ListNode,String> nodeHashMap= new HashMap<ListNode,String>();
//        Map nodeHashMap2= new HashMap<>();
        //错误写法,containsKey()用法的理解错误
        /*while(listNode1 != null){
            nodeHashMap1.put(listNode1,null);
            listNode1 = listNode1.next;
        }
        while(listNode2 != null){
            nodeHashMap2.put(listNode2,null);
            listNode2 = listNode2.next;
        }
        boolean b = nodeHashMap1.containsKey(nodeHashMap2);*/
        ListNode node1 = listNode1;
        ListNode node2 = listNode2;
        //怎么循环呢?将链表的数据存入map中,难点在与 每次存入节点后,都要指向下个节点,
        //每次存入节点,比如节点1-->2-->3,第一个map存入1-->2--3,第二个map存入2--3,第三个map存入3
        while(node1 != null){
            nodeHashMap.put(node1,null);
            node1 = node1.next;
        }
        while(node2 != null){
           if (nodeHashMap.containsKey(node2)){
               return node2;
           }
            node2 = node2.next;
        }
        return null;
    }

    /**
     * hashSet方法解析 两个链表,找出它们的第一个公告子节点
     * @param listNode1
     * @param listNode2
     * @return
     */
    public static ListNode hhztFindFirstCommonNodeBySet(ListNode listNode1,ListNode listNode2){
        Set<ListNode> listNodeSet = new HashSet<ListNode>();
        ListNode node1 = listNode1;
        ListNode node2 = listNode2;
        while(node1 != null){
            listNodeSet.add(node1);
            node1 = node1.next;
        }
        while(node2 != null){
           if ( listNodeSet.contains(node2)){
               return node2;
           }
           node2 = node2.next;
        }
        return null;
    }

/**
     * 简单构造两个链表,直接引用数据
     *
     * @return
     */
    private static ListNode[] initLinkedList() {
        ListNode[] heads = new ListNode[2];
//        构造第一个链表交点之前的元素 1 ->2-> 3
        heads[0] = new ListNode(1);
        ListNode current1 = heads[0];
        current1.next = new ListNode(2);
        current1 = current1.next;
        current1.next = new ListNode(3);
        current1 = current1.next;

//        构造第二个链表交点之前的元素11->22
        heads[1] = new ListNode(11);
        ListNode current2 = heads[1];
        current2.next = new ListNode(22);
        current2 = current2.next;
//        构造公共交点以及之后的元素

        ListNode node4 = new ListNode(4);
        current1.next = node4;
        current2.next = node4;
        ListNode node5 = new ListNode(5);
        node4.next = node5;


        ListNode node6 = new ListNode(6);
        node5.next = node6;

        return heads;
    }

1-2.栈

栈的基本用法:

push方法:添加元素
当栈为空的时候,top 为 -1 ,当只有 1 个元素时,top 为 0 
stack.push()
pop方法:弹出栈顶元素
stack.pop()
peek方法:获取栈顶元素 
stack.peek()

代码如下(示例):

 /**
     * 栈 方法解析 两个链表,找出它们的第一个公告子节点
     * @param listNode1
     * @param listNode2
     * @return
     */
    public static ListNode hhztFindFirstCommonNodeByStack(ListNode listNode1,ListNode listNode2){
        Stack<ListNode> listNodeStack = new Stack<ListNode>();
        Stack<ListNode> listNodeStack2 = new Stack<ListNode>();
        ListNode node1 = listNode1;
        ListNode node2 = listNode2;
        while(node1 != null){
            listNodeStack.push(node1);
            node1 = node1.next;
        }
        while(node2 != null){
            listNodeStack2.push(node2);
            node2 = node2.next;
        }
        ListNode nodeNew = null;
        //错误,忘记进行循环比较
//        if (listNodeStack.peek()==listNodeStack2.peek()){
//           nodeNew = listNodeStack.pop();
//           listNodeStack2.pop();
//        }
        while(listNodeStack.size()>0&&listNodeStack2.size()>0){
            //需要在加深理解下,栈顶数据
            if (listNodeStack.peek()==listNodeStack2.peek()){
                nodeNew = listNodeStack.pop();
                listNodeStack2.pop();
            }else {
                break;
            }
        }

        return nodeNew;

    }

2.判断链表是否为回文序列

首先理解什么是回文序列:
如果一个数字序列逆置之后跟原序列是一样的,就称这样的数字序列为回文序列。
例如:
{1, 2, 1}, {15, 78, 78, 15} , {112} 是回文序列,
{1, 2, 2}, {15, 78, 87, 51} ,{112, 2, 11} 不是回文序列。

判断链表是否为回文序列

示例:
     输入:1->2->2->1
      输出:true
      进阶:你能否用O(n)时间复杂度和O(1)空间复杂度解决此题?
package com.yugutou.charpter1;

import com.yugutou.charpter1_linklist.level2.topic2_2回文序列.IsPalindromic;

import java.util.Stack;

/**
 * 判断链表是否为回文序列
 * 示例:
 * 输入:1->2->2->1
 * 输出:true
 * 进阶:你能否用O(n)时间复杂度和O(1)空间复杂度解决此题?
 */
public class HhztIsPalindromic {

    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4, 4, 3, 2, 1};
        ListNode node = initLinkedList(a);
        //全部压栈
        boolean b = hhztIsPalindromeByAllStack(node);
        System.out.println(b);
    }

   static class ListNode{
        public int val;
        public ListNode next;

        public ListNode(int a){
            val = a;
            next = null;
        }
    }

    public static ListNode initLinkedList(int[] array){
        //错误的初始化
//        ListNode head =null;
//        for (int i = 1; i < array.length; i++) {
//            head = new ListNode(i);
//            if (i==1){
//                head.val = i;
//            }else {
//                head = head.next;
//            }
//        }
        ListNode head =null,cur =null;
        for (int i = 0; i < array.length; i++) {
            ListNode newNode = new ListNode(array[i]);
            if (i==0){
                head = newNode;
                cur = head;
            }else{
               cur.next = newNode;
               cur = newNode;
            }
        }
        return head;
    }

    /**
     * 全部压栈   判断链表是否为回文序列
     */
    public static boolean hhztIsPalindromeByAllStack(ListNode node){
        //为什么不能使用listNode的泛型进行比较呢?
        //Stack listNodeStack = new Stack();
        Stack<Integer> listNodeStack = new Stack<Integer>();
        ListNode head = node;
        while (head !=null){
            listNodeStack.push(head.val);
            head = head.next;
        }
        //如何将栈中的元素进行对比呢? 思路陷入盲区,其实思考下栈的原理就能明白,先入后出,
        // 栈目前的顺序是和原始的链表顺序是相反的,我们可以进行直接出栈对比
        while (node!=null){
            //将链表的每个数据与另外一组出栈的数据进行比较
            if (node.val != listNodeStack.pop()){
                return false;
            }
            node = node.next;
        }
        return true;
    }

    /**
     * 压栈一半 判断链表是否为回文序列
     * 与全部压栈的主要区别是 Java中>>,>>=,<<,<<=运算符的使用方式
     * @param node
     * @return
     */
    public static boolean hhztIsPalindromeByHalfStack(ListNode node){
        Stack<Integer> listNodeStack = new Stack<Integer>();
        ListNode head = node;
        //创建一个计数的参数
        int count =0;
        while (head !=null){
            listNodeStack.push(head.val);
            head = head.next;
            count++;
        }
         //首先我们要了解 Java中>>,>>=,<<,<<=运算符的作用
        /**
          1.Java语言中的 >> 意思为:逻辑右移,相当于除以2
          如:8>>1、8>>2

          8>>1,则是将数字8右移1位,结果为8/=4
          8>>2,则是将数字8右移2位,结果为8/=2

          2.Java语言中的 >>= 意思为:右移后赋值,相当于除以2
          如:当x = 8时,

          x >>= 2,则是x = 8/,结果x =2
          x >>= 3,则是x = 8/,结果x =1

          3.Java语言中的 << 意思为:逻辑左移,相当于乘2
          如:2<<1、3 << 2、3<<3

          2 << 1,则是将数字2左移1位,结果为2*=4
          3 << 2,则是将数字3左移2位,结果为3*=12
          3 << 3,则是将数字3左移3位,结果为3*=24

          4.Java语言中的 <<= 意思为:左移后赋值,相当于乘2后赋值
          如:当x = 5时,

          x <<= 3,则是x = 5*,结果x = 40
          ————————————————
          版权声明:本文为CSDN博主「Java大数据运动猿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
          原文链接:https://blog.csdn.net/m0_51697147/article/details/128601518
         */
        //计数的长度除以2
        count >>=1;
        //如何将栈中的元素进行对比呢? 思路陷入盲区,其实思考下栈的原理就能明白,先入后出,
        // 栈目前的顺序是和原始的链表顺序是相反的,我们可以进行直接出栈对比
        while (count-- >=0){
            //将链表的每个数据与另外一组出栈的数据进行比较
            if (node.val != listNodeStack.pop()){
                return false;
            }
            node = node.next;
        }
        return true;
    }
}


3.合并有序列表


待完善todo

判断链表是否为回文序列的实现方式,双指针,递归

你可能感兴趣的:(算法通关村,算法,链表,数据结构)