当前位置:   article > 正文

Java复习常用的数据结构和常用面试题之数组和链表_数组链表面试题

数组链表面试题


前言

提示:算法学习不努力,秋招春招是弟弟


提示:以下是本篇文章正文内容来自极客时间系列教程,下面案例可供参考

1. 数组、链表(Array、Linked List)

Array:在内存中,数组是一块连续的区域
Linked List:链表在内存中可以存在任何地方,不要求连续。

Array特点(数组)

1.读取快

在这里插入图片描述

2.插入和删除慢

在这里插入图片描述
Array由于内存连续的特点,插入和删除往往需要一个个往后挪位置,除非你插入的是最后一个位置。

时间复杂度

Array
• Access: O(1)
• Insert: 平均 O(n)
• Delete: 平均 O(n)
  • 1
  • 2
  • 3
  • 4

Linked List特点(链表)

单链表内存模型

在这里插入图片描述
每一个节点都保存了下一个数据的内存地址,通过这个地址找到下一个数据。
在这里插入图片描述
可以知道,这种数据结构的查找需要一个一个节点的寻址下去,效率是比较低的

插入和删除

插入操作

先让新增节点的下一节点指针域指向原有的尾部,再挂到要插入的节点
  • 1

在这里插入图片描述

删除操作
在这里插入图片描述
直接跳过要删除的节点,指向下一节点即可

时间复杂度

时间复杂度
space       O(n)
prepend 	O(1)
append      O(1)
lookup      O(n)
insert      O(1)
delete      O(1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

特点总结

数组(Array)链表(Linked List)
读取O(1)O(n)
插入O(n)O(1)
删除O(n)O(1)
各自的优缺点

数组的优点:

  • 随机访问性强
  • 查找速度快

数组的缺点

  • 插入和删除效率低
  • 可能浪费内存
  • 内存空间要求高,必须有足够的连续内存空间。
  • 数组大小固定,不能动态拓展

链表的优点

  • 插入删除速度快
  • 内存利用率高,不会浪费内存
  • 大小没有固定,拓展很灵活。

链表的缺点

  • 不能随机查找,必须从第一个开始遍历,查找效率低

实战题目

  1. 206.反转链表:https://leetcode.com/problems/reverse-linked-list/
  2. 24.两两交换链表中的节点:https://leetcode.com/problems/swap-nodes-in-pairs
  3. 141.环形链表:https://leetcode.com/problems/linked-list-cycle
  4. 142.环形链表II:https://leetcode.com/problems/linked-list-cycle-ii
  5. 25.K 个一组翻转链表https://leetcode.com/problems/reverse-nodes-in-k-group/

个人解题思路

自定义ListNode

package LinkedList;

public class ListNode {
   //数据域
   public int val;

   //下一节点域
   public ListNode next;

   ListNode() {
   }
   ListNode(int val) {
       this.val = val;
   }
   ListNode(int val, ListNode next) {
       this.val = val;
       this.next = next;
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
206.反转链表

迭代思路
在这里插入图片描述
递归思路
在这里插入图片描述

package com.gdpu.day1;

import LinkedList.ListNode;

/**
 * 反转链表
 * https://leetcode.com/problems/reverse-linked-list/
 *
 */

public class NO_206_ReverseLinkedList {

    //迭代解法
    public ListNode reverseList(ListNode head){
        //前面的指针
        ListNode prev =null;
        //当前指针
        ListNode curr = head;
        //当还没到最后一个指针
        while (curr!=null){
            //反转一个的操作
            ListNode next = curr.next;
            curr.next = prev;
            //向后移动
            prev =curr;
            curr =next;
        }
        return prev;
    }

    //递归解法
    public ListNode recursive(ListNode head){
        //递归终止条件
        //head是null,或者已经到最后一个节点,出口
        if (head ==null ||head.next==null){
            return head;
        }
        ListNode p = recursive(head.next);
        //一个环形指回前面
        head.next.next = head;
        //把目前的下一个设为null
        head.next =null;
        return p;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
24.两两交换链表中的节点

递归思路

利用栈的先进后出的特性
在这里插入图片描述

package com.gdpu.day1;

import LinkedList.ListNode;

import java.util.Stack;

public class NO_24_swapPairs {

    /**
     * 递归出口:当前节点或者下一个节点为空,返回
     * 方法内容:当前节点next,指向当前节点,指针互换
     * 返回值:返回交换完成的节点
     */
    public ListNode swapPairsRecursive(ListNode head) {
        //递归的终止条件
        if(head==null || head.next==null) {
            return head;
        }
        //假设链表是 1->2->3->4
        //这句就先保存节点2
        ListNode tmp = head.next;
        //继续递归,处理节点3->4
        //当递归结束返回后,就变成了4->3
        //于是head节点就指向了4,变成1->4->3
        head.next = swapPairsRecursive(tmp.next);
        //将2节点指向1
        tmp.next = head;
        return tmp;
    }

    //用栈
    public ListNode swapPairsByStack(ListNode head) {
        if(head==null || head.next==null) {
            return head;
        }
        //用stack保存每次迭代的两个节点
        Stack<ListNode> stack = new Stack<ListNode>();
        ListNode p = new ListNode(-1);
        ListNode cur = head;
        //head指向新的p节点,函数结束时返回head.next即可
        head = p;
        while(cur!=null && cur.next!=null) {
            //将两个节点放入stack中
            stack.add(cur);
            stack.add(cur.next);
            //当前节点往前走两步
            cur = cur.next.next;
            //从stack中弹出两个节点,然后用p节点指向新弹出的两个节点
            p.next = stack.pop();
            p = p.next;
            p.next = stack.pop();
            p = p.next;
        }
        //注意边界条件,当链表长度是奇数时,cur就不为空
        if(cur!=null) {
            p.next = cur;
        } else {
            p.next = null;
        }
        return head.next;
    }


}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
141.环形链表

利用set集合的不可重复的特性

public boolean hasCycle(ListNode head) {
        Set<ListNode> seen = new HashSet<ListNode>();
        while (head != null) {
            if (!seen.add(head)) {
                return true;
            }
            head = head.next;
        }
        return false;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

快慢双指针迭代

/**
     * 快慢指针迭代
     */
    public boolean hasCycle1(ListNode head) {
        //如果是空或者只有一个节点
        if (head == null || head.next == null)
            return false;
        //起点差一步
        ListNode slow = head;
        ListNode fast = head.next;

        //起点快,步数快,还能相遇肯定有环
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return false;
            }
            //慢的走一步
            slow = slow.next;
            //快的走两步
            fast = fast.next.next;
        }
        return true;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
142.环形链表II
  • 利用set集合不可重复的特性
public ListNode detectCycle(ListNode head) {
        if(head==null||head.next==null){
            return null;
        }
        Set<ListNode> seen = new HashSet<>();
        while(head!=null){
            if(!seen.add(head)){
                return head;
            }
            head = head.next;
        }
        return null;        
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 利用双指针


在这里插入图片描述

/**
     * 用双指针
     */
    public ListNode detectCycle1(ListNode head) {
        ListNode fast = head, slow = head;
        while (true) {
            if (fast == null || fast.next == null) return null;
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) break;
        }
        fast = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return fast;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
25. K 个一组翻转链表

递归解决
在这里插入图片描述

package com.gdpu.day1;

import LinkedList.ListNode;

import java.util.List;

public class NO_25_reverseKGroup {
    public ListNode reverseKGroup(ListNode head, int k) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode tail = head;
        for (int i = 0; i < k; i++) {
            //剩余数量小于k的话,则不需要反转。
            if (tail == null) {
                return head;
            }
            tail = tail.next;
        }
        // 反转前 k 个元素
        ListNode newHead = reverse(head, tail);
        //下一轮的开始的地方就是tail
        head.next = reverseKGroup(tail, k);

        return newHead;
    }
    /**
     * 反转某段链表
     */
    private ListNode reverse(ListNode head,ListNode tail){
        ListNode prev=null;
        ListNode curr = head;
        while (curr!=tail){
            ListNode next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

进阶:栈和队列

Java复习常用的数据结构和常用面试题之栈和队列

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/642390
推荐阅读
相关标签
  

闽ICP备14008679号