链表的常见笔试题

1. 请将给定数组,转换成链表

private static Node arrayToLinkedList(int[] arr) {
       //需要设置两个节点, 一个头节点,一个尾节点
       //初始情况下两个都为空
       Node head = null;
       Node tail = null;
       //通过 for each 依次把节点尾插到链表中
       for(int x : arr){
           Node newNode = new Node(x);
           //初始都为 null 的情况下
           // head 和 tail应该都指向同一个节点
           if (head == null) {
               head = newNode;
               tail = newNode;
           }else{
               //每次插入完毕后都要把 tail 更新
            tail.next = newNode;
            tail = tail.next;
           }
       }
       return head;
    }

2. 删除链表中等于给定值 val 的所有节点。

在这里插入图片描述

private static Node removeElements(Node head, int val) {
     //判断为空链表的请况
      if(head == null){
          return null;
      }
      //一般情况
      //需要注意的是,
      // 把判断头结点是否为要删除元素的代码放在前面会导致第二个节点无法被判断
      //所以要先处理一般情况, 后处理头结点
      Node pre = head;
      Node cur = head.next;
      while(cur != null){
          if(cur.val == val){
              //找到了, 开始删除元素
              pre.next = cur.next;
              cur = pre.next;
          }else{
              //没有找到, 更新 pre 的值和 cur 的值
              pre = pre.next;
              cur = cur.next;
          }
      }
      //判断链表头结点为要删除的元素的情况
      if(head.val== val){
          head = head.next;
      }
      return head;
    }

3. 反转一个单链表

在这里插入图片描述
在这里插入图片描述

private static Node reverseList(Node head) {
        //判断空链表
        if(head == null){
            return null;
        }
        //判断链表只有一个元素
        if(head.next == null){
            return head;
        }
        //处理一般情况
        Node newHead = null;
        Node preNode = null;
        Node curNode = head;
        while(curNode != null){
            Node nextNode = curNode.next;
            if(nextNode == null){
                //此时已经到达末尾
                //更新头结点位置
                newHead = curNode;
            }
            //开始反转
            curNode.next = preNode;
            //更新位置
            preNode = curNode;
            curNode = nextNode;
        }

4.给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

在这里插入图片描述

public ListNode middleNode(ListNode head) {
        //0.处理特殊情况
        if(head == null){
            return null;
        }
        //1.先计算出链表长度size
        int size = 0;
        ListNode cur = head;
        for(; cur != null; cur = cur.next){
            size++;
        }
        //2.找到中间节点
        int mid = size/2;

        //3.寻找中间节点并返回
        cur = head;
        for(int i = 0; i < mid; i++){
            cur = cur.next;
        }
        return cur;
    }

5. 输入一个链表,输出该链表中倒数第k个结点。

在这里插入图片描述

public ListNode FindKthToTail(ListNode head,int k) {
        //0.处理特殊情况
        if(head == null){
           return null;
        }
        //1.计算链表长度size并判断k的合法性
        int size = 0;
        ListNode cur = head;
        for(; cur != null; cur = cur.next){
            size++;
        }
        if(k < 0 || k > size){
            return null;
        }
        //2.寻找第size-k+1个节点并返回
        cur = head;
        for(int i = 1; i < size-k+1; i++){
            cur = cur.next;
        }
        return cur;

6. 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

在这里插入图片描述

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //0.判断特殊情况
        if(l1 == null){
            return l2;
        }
        if(l2 == null){
            return l1;
        }

        //1.创建一个新链表用来存放两个链表中的节点
        ListNode newHead = new ListNode(0);
        ListNode newCur = newHead;

        //2.遍历两个链表并一一比较将小者先放到新链表中直到有一个链表结束
        ListNode cur1 = l1;
        ListNode cur2 = l2;
        while(cur1 != null && cur2 != null){
            if(cur1.val < cur2.val){
                newCur.next = cur1;
                cur1 = cur1.next;
            }else{
                newCur.next = cur2;
                cur2 = cur2.next;
            }
            newCur = newCur.next;
        }

        //3.将剩下一个链表中的剩下节点全部放到新链表中
        if(cur1 == null){
            newCur.next = cur2;
        }else{
            newCur.next = cur1;
        }
        
        //4.返回新链表的头结点(注意因为新建的链表是带有傀儡节点的链表所以需要返回头结点.next)
        return newHead.next;

7. 现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

public ListNode partition(ListNode pHead, int x) {
        //0.处理特殊情况
        if(pHead == null){
            return null;
        }
        //1.创建两个新链表(带傀儡节点) 分别用来存放小于x的节点和大于x的节点
        ListNode smaller = new ListNode(0);
        ListNode sCur = smaller;
        ListNode bigger = new ListNode(0);
        ListNode bCur = bigger;
        //2.遍历原链表, 遇到比x小的就往小链表后接, 大的就往大链表后面接
        ListNode cur = pHead;
        while(cur != null){
            if(cur.val < x){
               sCur.next = cur;
               sCur = sCur.next;
            }else{
                bCur.next = cur;
                bCur = bCur.next;
            }
            cur = cur.next;
        }
        //3.原链表遍历完毕后将大小链表首尾相接并把大链表的末尾设成null
        sCur.next = bigger.next;
        bCur.next = null;
        //4.返回小链表的头结点.next
        return smaller.next;

8.在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

在这里插入图片描述

public ListNode deleteDuplication(ListNode pHead) {
        //0. 处理特殊情况
        if(pHead == null || pHead.next == null){
            return pHead;
        }
        //1. 创建一个新链表用来存不重复的节点
        ListNode newHead = new ListNode(0);
        ListNode newCur = newHead;
        ListNode cur = pHead;
        //2. 遍历整个链表, 分别比较当前节点和后面一个节点是否相同
        //3. 如果不同将该节点放到新链表中, 如果相同则继续便利直到遇到与之不同的节点
        while(cur != null){
            if(cur.next != null && cur.val == cur.next.val){
                while(cur != null && cur.next != null 
                      && cur.val == cur.next.val){
                    cur = cur.next;
                }
            }else{
                newCur.next = new ListNode(cur.val);
                newCur = newCur.next;
            }
            cur = cur.next;
        }
        //4. 返回新链表的头结点.next
        return newHead.next;

9.给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。

在这里插入图片描述

public boolean chkPalindrome(ListNode A) {
        //0. 判断特殊情况
        if(A == null || A.next == null){
            return true;
        }
        //1. 将原链表拷贝一份
        ListNode newHead = new ListNode(0);
        ListNode newCur = newHead;
        for(ListNode cur = A; cur != null; cur = cur.next){
            newCur.next = new ListNode(cur.val);
            newCur = newCur.next;
        }
        ListNode B = newHead.next;
        //2. 反转拷贝后的链表
        ListNode preNode = null;
        ListNode curNode = B;
        while(curNode != null){
            ListNode nextNode = curNode.next;
            if(nextNode == null){
                B = curNode;
            }
            curNode.next = preNode;
            preNode = curNode;
            curNode = nextNode;
        }
        //3. 将原链表和反转后的链表一一比较
        //   如果有不同的就返回false反之返回true
        ListNode cur1 = A;
        ListNode cur2 = B;
        while(cur1 != null && cur2 != null){
            if(cur1.val != cur2.val){
               return false;
            }else{
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
        }
        return true;

10.编写一个程序,找到两个单链表相交的起始节点。

在这里插入图片描述

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //0. 判断特殊情况
        if(headA == null || headB == null){
            return null;
        }
        //1. 分别求出两个链表的长度
        int size1 = 0;
        for(ListNode cur = headA; cur != null; cur = cur.next){
            size1++;
        }
        int size2 = 0;
        for(ListNode cur = headB; cur != null; cur = cur.next){
            size2++;
        }
        //2. 先让长链表走 长-短 步
        if(size1 > size2){
            for(int size = 0; size < size1-size2; size++){
                headA = headA.next;
            }
        }else{
            for(int size = 0; size < size2-size1; size++){
                headB = headB.next;
            }
        }
        //3. 这时两个链表是相同起点 再让他俩一起走相同步数 相遇的点就是相交节点
        while(headA != null && headB != null){
            if(headA == headB){
                return headB;
            }
            headA = headA.next;
            headB = headB.next;
        }
        return null;

11.给定一个链表,判断链表中是否有环。

在这里插入图片描述

public boolean hasCycle(ListNode head) {
        //0.判断特殊情况
        if(head == null || head.next == null){
            return false;
        }
        //1. 创建快慢指针
        ListNode fast = head;
        ListNode slow = head;
        //2.快指针一次走两步慢指针一次走一步 若快慢指针重合 则有环
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                return true;
            }
        }
        return false;

12. 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

在这里插入图片描述

public ListNode detectCycle(ListNode head) {
    //核心算法就是分别从链表头结点开始到入环扣 和 在环内相遇的节点开始到入环口的距离相等
        //0. 判断特殊情况
        if(head == null || head.next == null){
            return null;
        }
        //1. 创建快慢指针
        ListNode fast = head;
        ListNode slow = head;
        //2. 快指针一次走两步慢指针一次走一步 记录两者相遇的点meet
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                break;
            }
        }
        //3.判断链表是否不带环
        if(fast == null || fast .next == null){
            return null;
        }
        //4. 分别从链表头结点和meet开始一起走相同步数, 两者相遇的点就是带环链表的入环口
        ListNode cur = head;
        ListNode meet = fast;
        while(cur != meet){
            cur = cur.next;
            meet = meet.next;
        }
        return cur;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值