顺序表与链表

前言:

顺序表和链表是属于数据结构中比较基础的知识,我们需要对其进行掌握。在JAVA原生标准库中分别为ArrayList和LinkedList。下图是整个数据结构之间的结构框图

1.ArrayList

背后用来存储数据的是一个数组,所以用ArrayList来进行相关操作时都是对该数组进行操作。ArrayList是一个线性表并且ArrayList实现了List接口。

(1)构造方法:

主要就这两种:

可以看到第一种构造方法,并没有指定顺序表的大小,在源码中亦是如此: 

 我们平时一般都是直接用的ArrayList的无参构造方法,在使用add方法的时候又是正确的,这是为什么?虽然在这里没有进行顺序表大小的分配,但是在add操作时会进行大小的分配:

可以看到当s == elementData.length时,elementData进行了扩容,然后就可以往里添加元素了。

(2)操作:

 解释:

add操作:

ps1:对顺序表进行添加元素,必然在添加到某个时候,就满了(因为背后就是一个数组),下图是add中扩容的原理(grow方法)。

通过grow方法,当数组放满了,会对数组进行1.5倍的扩容 。

addAll操作:

?代表通配符,传入的参数c是否为E的子类或者本身,然后就是c必须实现Collection接口,否则这个方法不能使用。

(3)OJ题:

杨辉三角

题析:题目告诉我们最终要返回的是一个二维的顺序表(二维数组),所以这个题要使用顺序表来做。首先我们从第一行看,只有一个元素1,与其他行都与众不同,所以这一行(也是一个一维的顺序表)直接放入二维顺序表中。然后观察后面所有行,最前面都有一个元素1,最后面都有一个元素1,中间的元素是由上一行元素和上一行元素的前一个元素相加得来,此时这个题就迎刃而解了。

题解:

 public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> lists = new ArrayList<>();
        if(numRows == 0) {
            return lists;
        }
        List<Integer> list0 = new ArrayList<>();
        list0.add(1);
        lists.add(list0);
        //从第一行开始放
        for(int i = 1; i < numRows; i++) {
            List<Integer> list = new ArrayList<>();
            //开头
            list.add(1);
            //中间
            for(int j = 1; j < i; j++) {
                int value1 = lists.get(i-1).get(j);
                int value2 = lists.get(i-1).get(j-1);
                list.add(value1+value2);
            }
            //结尾
            list.add(1);
            lists.add(list);
        }
        return lists;
 }

2.LinkedList

(1)链表结构:

链表结构有很多种:带头/不带头,单向/双向,循环/非循环,而JAVA标准库中的LinkedList是不带头双向非循环的链表。链表的背后是由很多节点构成的,这些节点又由数据域(存储元素)和指针域(存储下一个节点)构成,默认最后一个节点不指向任何一个节点,所以为最后一个节点的指针域为null。

(2)构造方法:

一般只使用无参构造的方法。它不需要分配大小,它的大小是动态变化的。

(3)操作:

解释:

addFirst操作:

它的时间复杂度是O(1),不论是双向还是单向链表。 

addLast操作:

如果链表是单向的话,时间复杂度就是O(n)(n为链表的大小);如果是双向的话就是O(1)。这里LinkedList进行尾插的操作所需要的时间复杂度就是O(1)。

(4)OJ题:

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

题解:

 public ListNode removeElements(ListNode head, int val) {
        if(head == null) {
            return head;
        }
        ListNode prev = head;
        ListNode cur = head.next;
        while(cur != null) {
            if(cur.val == val) {
                prev.next = cur.next;
                cur = cur.next;
            }else {
                prev = cur;
                cur = cur.next;
            }
        }
        //这句代码写在最前面就需要while进行,以防头节点是要移除的节点,下一个也是要移除的。
        if(head.val == val) {
            head = head.next;
        }
        return head;
 }

2.反转一个单链表。

题析:

题解:

 public ListNode reverseList(ListNode head) {
        if(head == null) {
            return null;
        }
        if(head.next == null) {
            return head;
        }
        ListNode cur = head.next;
        
        head.next = null;
        while(cur != null) {
            ListNode curNext = cur.next;
            cur.next = head;
            head = cur;
            cur = curNext;  
        }
        return head;
 }

3.返回中间节点。

题析:

题解:

 public ListNode middleNode(ListNode head) {
        if(head == null) {
            return head;
        }
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
 }

4.链表的回文结构

题析:

题解:
 

  public boolean chkPalindrome(ListNode A) {
        if(A == null) {
            return true;
        }
        ListNode fast = A;
        ListNode slow = A;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        //开始翻转
        ListNode cur = slow.next;
        while(cur != null) {
            ListNode curNext = cur.next;
            cur.next = slow;
            slow = cur;
            cur = curNext;
        }
        
        while(slow != A) {
            if(slow.val != A.val) {
                return false;
            }
            //这种情况是偶数个元素的判断
            if(A.next == slow) {
                return true;
            }
            slow = slow.next;
            A = A.next;
        }
        return true;
        
    }

3. 顺序表与链表的对比

(1)如果某个背景下需要进行频繁的随机元素访问,那么选用顺序表

(2)如果要进行频繁的插入或者删除某个元素,那么选用链表。

(3)顺序表的大小是事先指定好了的,如果满了则进行扩容,而链表不需要指定大小,添加一个元素,链表的长度加1。

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值