顺序表、链表(ArrayList、LinkedList)

目录

前言:

顺序表(ArrayList):

顺序表的原理:

ArrayList源码:

的含义:​编辑

ArrayList的相关方法:​编辑

向上转型List:

练习题(杨辉三角):

扑克牌游戏:

链表(LinkedList):

链表的原理:

自定义链表的实现:

LinkedList源码:

LinkedList使用注意事项:

练习题(判断是否是会问链表):

迭代器(Iterator):

总结:


前言:

本篇我们来讲解数据结构中的顺序表和顺序表,因为Java有集合框架,所以可以直接使用类创建对象来完成。

顺序表(ArrayList):

顺序表的原理:

顾名思义,就是有顺序的表,类是ArrayList,底层原理是一个数组,当到达最大容量时,会进行扩容。

当一个数组(顺序表)中存放的基本类型数据时,我们想要全部清除可以直接将有效下标置为0,这样再添加就是覆盖的效果。

但是如果数组中存放的是引用类型,则不能这样。因为基本类型都会默认初始化,比如整形会默认初始化为0。

此时引用类型不能直接将有效下标置为0,否则会发生内存泄漏。 因为引用类型开辟的空间没有回收,JVM中,回收算法有很多。最好通过for循环来置空。

java 复制代码
for() {
    elem[i] = null;
}

ArrayList源码:

接下来我们就来细致观察ArrayList源码: ArrayList底层就是顺序表,继承了AbstractList类,里面重写了toString方法。

ArrayList底层就是顺序表,继承了AbstractList类,里面重写了toString方法。

java 复制代码
public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(0,99);
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i) + " ");
    }

    System.out.println(list);
}

ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表。

我们再来看源码:

此时就分配了10个空间。

所以当使用无参构造器时,第一次添加元素是申请10个空间,之后每次到达上限以后就扩容1.5倍。

<? extends E>的含义:

因为ArrayList实现了Collection接口。

java 复制代码
public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);

        ArrayList<Number> list1 = new ArrayList<>(list);

        list1.add(99);
        list1.add(88);
        System.out.println(list1);
    }
}

ArrayList的相关方法:

java 复制代码
public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    System.out.println(list);

    ArrayList<Number> list1 = new ArrayList<>();
    list1.addAll(list);
    list1.remove(new Integer(2));//删除指定对象
    System.out.println(list1);

}

我们来观察一下代码:

java 复制代码
public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);

    List<Integer> list1 = list.subList(1, 3);//[1,3)
    list1.set(1,99);
    System.out.println(list);
    System.out.println(list1);
}

向上转型List:

java 复制代码
List<List<Integer>> list1 = new ArrayList<>();
list1.add(new ArrayList<>());
list1.add(new ArrayList<>());

观察这个代码,意思其实是有一个ArrayList类,每一个元素里面是一个ArrayList存放整形的引用类型(List是一个接口)。其实可以理解为二维数组。

练习题(杨辉三角):

java 复制代码
class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> ret = new ArrayList<>();
        List<Integer> list = new ArrayList<>();
        list.add(1);

        ret.add(list);

        for (int i = 1; i < numRows; i++) {
            List<Integer> cur = new ArrayList<>();
            cur.add(1);

            //获取上一行
            List<Integer> prev = ret.get(i - 1);
            for (int j = 1; j < i; j++) {
                int val = prev.get(j) + prev.get(j - 1);
                cur.add(val);
            }

            //将此行最后一个元素置为1
            cur.add(1);
            ret.add(cur);
        }
        return ret;
    }
}

扑克牌游戏:

java 复制代码
public class Test {
    public static void main(String[] args) {
        CardDome cardDome = new CardDome();
        List<Card> cardList = cardDome.buyCard();
        System.out.println("买的牌如下:");
        System.out.println(cardList);

        System.out.println("洗牌");
        cardDome.shuffle(cardList);
        System.out.println(cardList);

        System.out.println("揭牌:");
        cardDome.getCards(cardList);
    }
}
java 复制代码
public class CardDome {
    //定义四种花色
    private static final String[] suits = {"♥","♣","♦","♠"};

    //52张
    //J - 11  Q - 12  K - 13
    //买一副牌
    public List<Card> buyCard() {
        List<Card> cardList = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            for (int j = 1; j <= 13; j++) {
                Card card = new Card(suits[i], j);
                cardList.add(card);
            }
        }

        return cardList;
    }

    //洗牌
    public void shuffle(List<Card> cardList) {
        //生成随机数
        Random random = new Random();
        //从后向前打乱
        for (int i = cardList.size() - 1; i > 0; i--) {
            //有52张牌
            //第一次生成的就是 0 - 50 的随机数
            int index = random.nextInt(i);

            //index i 交换
            swap(cardList, i, index);
        }
    }

    private void swap(List<Card> cardList, int e1,int e2) {
        Card tmp = cardList.get(e1);
        cardList.set(e1,cardList.get(e2));
        cardList.set(e2,tmp);
    }

    //揭牌
    public void getCards (List<Card> cardList) {
        //3个人
        List<Card> hand1 = new ArrayList<>();
        List<Card> hand2 = new ArrayList<>();
        List<Card> hand3 = new ArrayList<>();

        List<List<Card>> hand = new ArrayList<>();
        hand.add(hand1);
        hand.add(hand2);
        hand.add(hand3);

        //3个人轮流抓牌5张牌
        for (int i = 0; i < 5; i++) {
            //j 代表人
            for (int j = 0; j < 3; j++) {
                Card card = cardList.remove(0);
                hand.get(j).add(card);
            }
        }

        System.out.println("第一个人揭牌如下:");
        System.out.println(hand1);
        System.out.println("第二个人揭牌如下:");
        System.out.println(hand2);
        System.out.println("第三个人揭牌如下:");
        System.out.println(hand3);

        System.out.println("剩下的牌:");
        System.out.println(cardList);
    }
}
java 复制代码
public class Card {
    private String suit;//花色
    private int rank;//数字

    public Card(String suit, int rank) {
        this.suit = suit;
        this.rank = rank;
    }

    public String getSuit() {
        return suit;
    }

    public void setSuit(String suit) {
        this.suit = suit;
    }

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    @Override
    public String toString() {
        return suit + ":" + rank + " ";
    }
}

链表(LinkedList):

链表的原理:

顺序表还是有缺点的,因为每次都是1.5倍扩容,所以有时量大时就会浪费内存,所以就衍生出了链表,不是连续内存,如果了解C语言中的链表,那么将是易如反掌。

自定义链表的实现:

我们定义head成员,是为了保存头结点。

java 复制代码
public class MySingleList {
    //节点使用内部类来写
    static class ListNode {
        public int val;
        public ListNode next;

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

    //定义一个链表的成员的头结点
    public ListNode head;

    public void createList() {
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(33);
        ListNode node3 = new ListNode(99);
        ListNode node4 = new ListNode(67);
        ListNode node5 = new ListNode(88);

        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;

        this.head = node1;
    }
}

LinkedList源码:

LinkedList的底层代码,可以发现这就是一个双向循环链表。 LinkedList的任意位置插入和删除元素师效率比较高,时间复杂度为O(1),比较适合任意位置插入的场景。

还有很多方法无法注意介绍,大家可以看Structure里面的所有相关方法、属性和内部类。因为效率高,所以底层只使用了双向链表,没有使用单向链表。

LinkedList使用注意事项:

当我们使用的是自定义链表时(就是没用Java框架),清空链表,可以直接将头尾置空,也可以逐个将其置空。因为Java有回收算法。

这里我们使用迭代器进行遍历,我们也可以反向遍历:

java 复制代码
public class Test2 {
    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        linkedList.add(1);
        linkedList.add(2);
        System.out.println(linkedList);

        //通过迭代器遍历链表
        ListIterator<Integer> it1 = linkedList.listIterator();
        while (it1.hasNext()) {
            System.out.print(it1.next() + " ");
        }
        System.out.println();

        System.out.println("===反向===");
        ListIterator<Integer> it2 = linkedList.listIterator(linkedList.size());
        while (it2.hasPrevious()) {
            System.out.print(it2.previous() + " ");
        }
    }
}

练习题(判断是否是会问链表):

java 复制代码
public class PalindromeList {
    public boolean chkPalindrome(ListNode head) {
        if (head == null || head.next == null) {
            return true;
        }
        // write code here
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        //翻转slow以后的链表
        ListNode cur = slow.next;
        while (cur != null) {
            ListNode nextPos = cur.next;
            //翻转(利用指针翻转)
            cur.next = slow;
            slow = cur;
            cur = nextPos;
        }

        //从前向后 从后向前
        while(head != slow) {
            if (slow.val != head.val) {
                return false;
            }
            if (head.next == slow) {
                return true;
            }
            head = head.next;
            slow = slow.next;
        }
        return true;

    }
}

迭代器(Iterator):

我们可以利用迭代器进行遍历:

java 复制代码
public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);

    Iterator<Integer> it = list.iterator();
    while(it.hasNext()) {
        System.out.print(it.next() + " ");
    }
}

总结:

这里我还是默认各位都有基础,希望可以对有基础的人有些帮助(更多的是记笔记),感谢各位支持。

相关推荐
努力也学不会java几秒前
【Java并发】深入理解synchronized
java·开发语言·人工智能·juc
TDengine (老段)几秒前
TDengine 数学函数 CEIL 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
Giser探索家2 分钟前
建筑物孪生模型:重构空间数字化格局,赋能智慧城市
大数据·人工智能·算法·重构·分类·云计算·智慧城市
LB211222 分钟前
Redis 黑马skyout
java·数据库·redis
Tiny番茄23 分钟前
leetcode 3. 无重复字符的最长子串
数据结构·python·算法·leetcode
豐儀麟阁贵28 分钟前
Java知识点储备
java·开发语言
hrrrrb34 分钟前
【Spring Security】Spring Security 密码编辑器
java·hive·spring
豐儀麟阁贵37 分钟前
2.3变量与常量
java·开发语言
兮动人2 小时前
Eureka注册中心通用写法和配置
java·云原生·eureka
爱编程的小白L4 小时前
基于springboot志愿服务管理系统设计与实现(附源码)
java·spring boot·后端