第二阶段-day02_Linked

今天我们主要将了数组和链表。

复习昨天

集合类:
    a.集合类可以自己调整大小
    b.只能存储引用类型的数据
    c.有丰富的API
    
Collection
    概述:
    API:
        增:
            boolean add(E e)
            boolean add(Collection c)
        删:
            void clear()
            boolean remove(Object o)
            boolean removeAll(Collection c)
            boolean retainAll(Collection c)
        查:
            boolean contains(Object o)
            boolean containsAll(Collection c)
        获取集合属性:
            int size()
            boolean isEmpty()
        遍历:
            Object[] toArray()
            Iterator iterator()

Iterator
    概述:
    设计原理:迭代器设计模式
    API:
        boolean hasNext()
        E next()
        void remove()
    注意事项:
        a. 警惕并发修改异常
        b. 用迭代器遍历的时候,不要使用while循环,可以使用for循环,最好使用foreach

List
    概述:
    API:
        增:
            void add(int index,E e)
            void addAll(int index, Collection c)
        删:
            E remove(int index)
        改:
            E set(int index, E e)
        查:
            E get(int index)
            int indexOf(Object o)
            int lastIndexOf(Object o)
        获取子串:
            List subList(int fromIndex, int toIndex)
        遍历:
            ListIterator listIterator()
            ListIterator listIterator(int index)

 

接着昨天的List讲

List subList(int fromIndex, int toIndex):获取子串,左包右不包

 1 package com.cskaoyan.list;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /*
 7  视图技术
 8  List subList(int fromIndex, int toIndex)
 9  */
10 public class ListDemo1 {
11     public static void main(String[] args) {
12         List list = new ArrayList();
13         list.add("hello");
14         list.add("world");
15         list.add("java");
16 
17         List subList = list.subList(1, 2);
18         System.out.println(subList); //[world]
19 
20         subList.set(0, "WORLD");
21         System.out.println(subList); //[WORLD]
22         System.out.println(list);//[hello, WORLD, java]
23     }
24 }

 发现list里的数据也改变了。→视图技术。

sublist与list公用同一份数据,修改sublist,list中的数据也会修改。

视图技术原理:sublist是以成员内部类的形式存在的,继承了AbstractList,这个抽象类实现了list接口。

∴sublist是以成员内部类的形式存在的,这样,它就可以访问外部类的数据。

视图技术应用场景:总公司的数据是分公司提交上来的。分公司的数据更改,总公司的数据也要改变。

 

 

List:
    ListIterator<E> listIterator()
    ListIterator<E> listIterator(int index)

  1 package com.cskaoyan.list;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 import java.util.ListIterator;
  6 
  7 /*
  8 List:
  9     ListIterator<E> listIterator()
 10     ListIterator<E> listIterator(int index)
 11 
 12 ListIterator接口
 13 概述:列表迭代器,允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。
 14 API:
 15     遍历:
 16         boolean hasNext()
 17         E next()
 18         boolean hasPrevious()
 19         E previous()
 20     获取位置:
 21         int previousIndex()
 22         int nextIndex()
 23     修改列表:
 24         void add(E e)
 25         void remove()
 26         void set(E e)
 27  */
 28 public class ListDemo2 {
 29     public static void main(String[] args) {
 30         List list = new ArrayList();
 31         list.add("hello");
 32         list.add("world");
 33         list.add("java");
 34 
 35         // ListIterator it = list.listIterator();
 36         /*ListIterator it = list.listIterator(2);
 37         System.out.println(it.next()); //java*/
 38 
 39         // 正向遍历
 40         /*for(ListIterator it = list.listIterator(); it.hasNext(); ){
 41             String s = (String) it.next();// 为什么会报错呢?
 42             System.out.println(s);
 43         }*/
 44 
 45         // 逆向遍历
 46 /*        for(ListIterator it = list.listIterator(list.size()); it.hasPrevious(); ) {
 47             String s = (String) it.previous();
 48             System.out.println(s);
 49         }*/
 50 
 51         // int previousIndex()
 52         // int nextIndex()
 53        /* ListIterator it = list.listIterator(1);
 54         // it.previous();
 55         it.next();
 56         it.next();
 57         System.out.println(it.previousIndex()); // 2
 58         System.out.println(it.nextIndex());// 3*/
 59 
 60 
 61        // void add(E e) 在哪个位置添加元素?光标后面
 62         /*ListIterator it = list.listIterator(2);
 63         it.add("HelloKitty");
 64         System.out.println(list);*/
 65 
 66         //练习:在world后面添加"HelloKitty"
 67         /*for(ListIterator it = list.listIterator(); it.hasNext(); ){
 68             String s = (String) it.next();
 69             // if ("world".equals(s)) it.add("HelloKitty");
 70             *//*if ("world".equals(s)) {
 71                 int index = it.nextIndex();
 72                 list.add(index, "HelloWorld");
 73             }*//*
 74         }
 75         System.out.println(list);*/
 76 
 77         //练习:在world前面添加"HelloKitty"
 78        /* for(ListIterator it = list.listIterator(list.size()); it.hasPrevious(); ) {
 79             String s = (String) it.previous();
 80             if ("world".equals(s)) it.add("HelloKitty");
 81         }
 82         System.out.println(list);*/
 83 
 84         // void remove() 删除哪个元素?删除的是最近返回的元素
 85         /*ListIterator it = list.listIterator();
 86         it.next();
 87         it.remove();
 88         System.out.println(list);*/
 89 
 90         /*ListIterator it = list.listIterator(list.size());
 91         it.previous();
 92         it.remove();
 93         System.out.println(list);*/
 94 
 95         // 如果最近没有返回元素,会怎样
 96         /*ListIterator it = list.listIterator();
 97         it.remove();*/ // IllegalStateException
 98 
 99         /*ListIterator it = list.listIterator();
100         it.next();
101         it.add("Allen");
102         it.remove(); //IllegalStateException
103         System.out.println(list);*/
104 
105         // 并发修改异常
106         /*for(ListIterator it = list.listIterator(); it.hasNext();) {
107             // if ("hello".equals(it.next())) it.remove();
108             if ("hello".equals(it.next())) {
109                 int index = it.previousIndex();
110                 list.remove(index);
111             }
112         }
113         System.out.println(list);*/
114 
115         // void set(E e) 替换哪个元素?最近返回的元素
116         // 练习:把"java"替换成"javaSE"
117         /*for(ListIterator it = list.listIterator(); it.hasNext();) {
118             // if ("java".equals(it.next())) it.set("javaSE");
119             if ("java".equals(it.next())) {
120                 int index = it.previousIndex();
121                 list.set(index, "javaSE");
122             }
123         }*/
124         ListIterator it = list.listIterator();
125         // it.set("javaSE"); // IllegalStateException
126         it.next();
127         it.remove();
128         it.set("javaSE"); // IllegalStateException
129         System.out.println(list);
130     }
131 }

 

常见的数据结构

  • 数组
  • 链表
  • 队列
  • 哈希表
数组
Q1: 数组我们都很熟悉,那你理解的数组是什么样的呢?它的最主要特点是什么呢?
Q2: 为什么数组的索引是一般都是从0开始的呢?
Q3: 为什么数组的效率比链表高?
 
Q1: 数组我们都很熟悉,那你理解的数组是什么样的呢?它的特点是什么呢?
A1:数组的本质是固定大小的连续的内存空间,并且这片连续的内存空间又被分割成等长的小空间。它最主要的特点是随机访问。
数组的长度是固定的
数组只能存储同一种数据类型的元素
注意:在Java中只有一维数组的内存空间是连续,多维数组的内存空间不一定连续。

那么数组又是如何实现随机访问的呢?
寻址公式:i_address = base_address + i * type_length
 
Q2: 为什么数组的索引是一般都是从0开始的呢?
假设索引不是从0开始的,而是从1开始的,那么我们有两种处理方式:
寻址公式变为: i_address = base_address + (i – 1) * type_length
浪费开头的一个内存空间,寻址公式不变。
在计算机发展的初期,不管是CPU资源,还是内存资源都极为宝贵,所以在设计编程语言的时候,索引就从0开始了,而我们也一直延续了下来。
 
Q3: 为什么数组的效率比链表高?
CPU、内存和IO设备,它们传输数据的速率是存在很大差异的。
那么根据木桶理论:木桶能装多少水,取决于最短的那块木板。程序的性能主要取决于IO设备的性能?也就是说,我们提升CPU和内存的传输速率收效甚微。
实际是这样的吗?当然不是!那我们是怎么解决它们之间的速率差异的呢?
CPU 和 内存
      高速缓存(预读)
      编译器的指令重排序
内存和 IO
      缓存:将磁盘上的数据缓存在内存。
CPU 和 IO
      中断技术
     
数组可以更好地利用CPU的高速缓存!
 
数组的基本操作
 
 

 

 

总结: 数组增删慢,查找快。
 
链表
形象地说,链表就是用一串链子将结点串联起来。
结点:包含数据域和指针域。
数据域:数据
指针域:下一个结点的地址
 
 
  结点    
  链表   

 

 链表的分类:

 

 

循环链表我们用的一般比较少,但是当处理的数据具有环形结构时,就特别适合用循环链表,比如约瑟夫问题。接下来我们讨论下单链表和双向链表。
单链表:

 

 总结:链表增删快,查找慢。

 

双向链表:
很容易验证,前面那些操作,双向链表和单链表的时间复杂度是一样的。那为什么在工程上,我们用的一般是双向链表而不是单链表呢 (比如JDK中的 LinkedList & LinkedHashMap)?
那自然是双向链表有单链表没有的独特魅力——它有一条指向前驱结点的链接

 

 

 

总结:虽然双向链表更占用内存空间,但是它在某些操作上的性能是优于单链表的。
思想:用空间换取时间。
缓存就是一种用空间换取时间的技术。
内存大小是有限的,所以缓存不能无限大。那么当缓存满的时候,再向缓存中添加数据,该怎么办呢?
缓存淘汰策略:
         ① FIFO (First In First Out)
         ② LFU (Least Frequently Used)
         ③ LRU (Least Recently Used)
 
LRU算法中我们就用到了链表!

练习:

  1. 求链表的中间元素
  2. 判断链表中是否有环
  3. 反转单链表

 

 1 package com.cskaoyan.exercise;
 2 
 3 public class Node {
 4     int value;
 5     Node next;
 6 
 7     public Node(int value) {
 8         this.value = value;
 9     }
10 
11     public Node(int value, Node next) {
12         this.value = value;
13         this.next = next;
14     }
15 }

1.求链表的中间元素

 1 package com.cskaoyan.exercise;
 2 /*
 3 求链表的中间元素
 4 示例:
 5 输入:1 --> 2 --> 3
 6 输出:2
 7 输入:1 --> 2 --> 3 --> 4
 8 输出:2
 9 
10 Q1:如果是数组,我们可以求中间元素?
11 arr[(arr.length-1) / 2]
12 Q2:
13     1.先求得长度length
14     2.再从头开始遍历,一边遍历,一边计算当前结点得索引。
15       当索引为 (length-1)/2 时,返回这个结点的值。
16  */
17 public class Ex1 {
18     public static int middleElement(Node head) {
19         // 求链表的长度
20         int length = 0;
21         Node x = head;
22         while (x != null) {
23             length++;
24             x = x.next; //x指向下一个结点
25         }
26         // 从头开始遍历,找索引为 (length-1)/2 的结点
27         int index = 0;
28         x = head;
29         while (index < (length - 1) / 2) {
30             index++;
31             x = x.next;
32         }
33         return x.value;
34     }
35 
36     public static void main(String[] args) {
37         // 1 --> 2 --> 3
38         /*Node head = new Node(3);
39         head = new Node(2, head);
40         head = new Node(1, head);
41         System.out.println(middleElement(head));*/
42 
43         // 1 --> 2 --> 3 --> 4
44 /*        Node head = new Node(4);
45         head = new Node(3, head);
46         head = new Node(2, head);
47         head = new Node(1, head);
48         System.out.println(middleElement(head));*/
49     }
50 }

 

2.判断链表中是否有环

 

 

 

 1 package com.cskaoyan.exercise;
 2 /*
 3 判断链表中是否有环
 4 1. 给一个阈值(10ms),如果在遍历链表的过程中10ms还没有结束,就认为有环。
 5 2. 迷雾森林
 6    Collection visited = new ArrayList();
 7  */
 8 public class Ex2 {
 9     public static boolean hasCircle(Node head) {
10 
11     }
12 }

 

posted @ 2020-05-15 00:32  dust--  阅读(127)  评论(0编辑  收藏  举报