第二十二篇 玩转数据结构——构建动态数组

1.. 数组基础

  • 数组就是把数据码成一排进行存放。
  • Java中,数组的每个元素类型必须相同,可以都为int类型,string类型,甚至是自定义类型。
  • 数组的命名要语义化,例如,如果数组用来存放学生的成绩,那么命名为scores就比较合适。
  • 索引(index)是数组中的一个重要概念,它是我们给数组中的每个元素分配的编号,从0开始,依次递增。如果数组中存放了n个元素,第一个元素的索引是0,最后一个元素的索引是n-1。
  • 通过索引,我们可以对数组中的元素进行快速访问,例如,我们访问索引为2的元素也就是数组中的第3个元素,就可以通过scores[2]这种形式。
  • 在Java中声明一个简单的数组
  • public class Main {
    
        public static void main(String[] args) {
            int[] arr = new int[10];
            for (int i = 0; i < arr.length; i++)
                arr[i] = i;
        }
    }
  • 声明一个有初始值的数组
  • public class Main {
    
        public static void main(String[] args) {
            int[] scores = new int[]{100, 99, 86};
            for (int i = 0; i < scores.length; i++)
                System.out.println(scores[i]);
        }
    }
  • for循环的另一种使用形式
  • public class Main {
    
        public static void main(String[] args) {
            int[] scores = new int[]{100, 99, 86};
            for (int score : scores)
                System.out.println(score);
        }
    }
  • 修改数组中的元素
  • socres[1] = 98;
  • 数组的索引可以是语义化的,也可以是非语义化的。
  • 数组的最大优点,就是可以通过索引对数据进行快速查询,因此,我们倾向于使用语义化的索引,这样我们就很清楚自己在查什么。
  • 如果我们的应用场景中,索引没有语义,那么使用其它数据结构可能是更好的选择。
  • 对于一些特殊应用场景,虽然使用了语义化索引,但依然不适合使用数组,例如,身份证号,我们不能使用身份证号来作为数组的索引,因为这个数字太大了,会导致巨大的空间浪费。
  • 如果数组的索引是非语义化的,我们就需要考虑很多问题,例如,当数组的空间未被填满时,如何表示空位处的元素?如何向数组中添加新的元素?如何删除掉数组中原有的元素?等等。Java所提供的原生数组是无法解决这些问题的,我们需要定制属于自己的数组类Array,即,基于Java的原生数组,二次封装属于我们自己的数组类。

2.. 实现自定义数组类Array所包含的基本方法:

  • public class Array {
    
        private int[] data;  //设置为private,不希望用户从外部直接获取这些信息,防止用户篡改数据
        private int size;
    
        //构造函数,传入数组的容量capacity构造Array
        public Array(int capacity) {
            data = new int[capacity];
            size = 0;
        }
    
        //无参数构造函数,默认数组容量capacity=10
        public Array() {
            this(10);    //这里的capacity是IDE自动添加的提示信息,实际不存在
        }
    
        //获取数组中的元素个数
        public int getSize() {
            return size;
        }
    
        //获取数组的容量
        public int getCapacity() {
            return data.length;
        }
    
        //判断数组是否为空
        public boolean isEmpty() {
            return size == 0;
        }
    }

3.. 实现向自定义数组中添加元素的方法

  • 向数组中添加元素的最简单的方法就是向数组的末尾添加元素
  • //向数组末尾添加一个新元素
    public void addLast(int e) {
    
        if (size == data.length) {
            throw new IllegalArgumentException("AddLast failed. Array is full.");
        }
    
        data[size] = e;
        size++;
    }
  • 向数组中指定索引位置插入一个元素
  • //在index位置插入一个新元素e
    public void add(int index, int e) {
    
        if (size == data.length) {
            throw new IllegalArgumentException("Add failed. Array is full.");
        }
    
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size");
        }
    
        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }
    
        data[index] = e;
        size++;
    }
  • 定义完向数组中指定索引位置插入一个元素的方法add之后,我们之前定义的向数组末尾插入元素的方法addLast其实可以调用add方法来实现,我们调整addLast方法如下:
  • //向数组末尾添加一个新元素
    public void addLast(int e) {
        add(size, e);
    }
  • 我们还可以调用add方法实现一个向数组开头添加一个新元素的方法
  • //向数组开头添加一个新元素
    public void addFirst(int e) {
        add(0, e);
    }

4.. 实现在数组中查询元素和修改元素的方法

  • 实现自定义打印数组格式
  • @Override
    public String toString() {    //覆盖父类的toString方法
    
        StringBuilder res = new StringBuilder();
    
        res.append(String.format("Array: size=%d, capacity=%d\n", size, data.length));
        res.append(‘[‘);
        for (int i = 0; i < size; i++) {
            res.append(data[i]);
            if (i != size - 1) {
                res.append(", ");
            }
        }
        res.append(‘]‘);
    
        return res.toString();
    }
  • 在main函数中进行测试:
  • public class Main {
    
        public static void main(String[] args) {
    
            Array arr = new Array(20);
            for (int i = 0; i < 10; i++) {
                arr.addLast(i);    //测试addLast方法
            }
            System.out.println(arr);
    
            arr.add(1, 100);     //测试add方法
            System.out.println(arr);
    
            arr.addFirst(-1);    //测试addFirst方法
            System.out.println(arr);
        }
    
    }
  • 打印效果如下:
  • Array: size=10, capacity=20
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=11, capacity=20
    [0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=12, capacity=20
    [-1, 0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 实现获取指定索引元素的方法
  • //获取index位置的元素
    public int get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Get failed. Index is illegal.");
        }
        return data[index];
    }
  • 实现修改指定索引元素的方法
  • //修改index位置的元素为e
    public void set(int index, int e) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Set failed. Index is illegal.");
        }
        data[index] = e;
    }
  • 实现查看数组中是否包含元素e的方法
  • //查找数组中是否存在元素e
    public boolean contains(int e) {
        for (int i = 0; i < size; i++) {
            if (data[i] == e) {
                return true;
            }
        }
        return false;
    }
  • 实现查看数组中指定元素的索引的方法,若找不到,返回-1
  • //查看数组中元素e的索引,若找不到元素e,返回-1
    public int find(int e){
        for(int i=0;i<size;i++){
            if(data[i] == e){
                return i;
            }
        }
        return -1;
    }
  • 实现删除数组中指定索引的元素的方法
  • //删除掉index位置的元素,并且返回所删除的元素
    public int remove(int index) {
    
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Add failed. Require index >= 0 and index < size");
        }
    
        int ret = data[index];
    
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        size--;
        return ret;
    }
    
    //删除掉数组开头的元素,并返回所删除的元素
    public int removeFirst() {
        return remove(0);
    }
    
    //删除掉数组末尾的元素,并返回所删除的元素
    public int removeLast() {
        return remove(size - 1);
    }
    
    //如果数组中有元素e,那么将其删除,否则什么也不做
    public void removeElement(int e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }

5.. 整理我们目前实现的业务逻辑:

  • public class Array {
    
        private int[] data;  //设置为private,不希望用户从外部直接获取这些信息,防止用户篡改数据
        private int size;
    
        //构造函数,传入数组的容量capacity构造Array
        public Array(int capacity) {
            data = new int[capacity];
            size = 0;
        }
    
        //无参数构造函数,默认数组容量capacity=10
        public Array() {
            this(10);    //这里的capacity是IDE自动添加的提示信息,实际不存在
        }
    
        //获取数组中的元素个数
        public int getSize() {
            return size;
        }
    
        //获取数组的容量
        public int getCapacity() {
            return data.length;
        }
    
        //判断数组是否为空
        public boolean isEmpty() {
            return size == 0;
        }
    
        //向数组末尾添加一个新元素e
        public void addLast(int e) {
            add(size, e);
        }
    
        //向数组开头添加一个新元素e
        public void addFirst(int e) {
            add(0, e);
        }
    
        //在index位置插入一个新元素e
        public void add(int index, int e) {
    
            if (size == data.length) {
                throw new IllegalArgumentException("Add failed. Array is full.");
            }
    
            if (index < 0 || index > size) {
                throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size");
            }
    
            for (int i = size - 1; i >= index; i--) {
                data[i + 1] = data[i];
            }
            data[index] = e;
            size++;
        }
    
        //获取index位置的元素
        public int get(int index) {
            if (index < 0 || index >= size) {
                throw new IllegalArgumentException("Get failed. Index is illegal.");
            }
            return data[index];
        }
    
        //修改index位置的元素为e
        public void set(int index, int e) {
            if (index < 0 || index >= size) {
                throw new IllegalArgumentException("Set failed. Index is illegal.");
            }
            data[index] = e;
        }
    
        //查找数组中是否存在元素e
        public boolean contains(int e) {
            for (int i = 0; i < size; i++) {
                if (data[i] == e) {
                    return true;
                }
            }
            return false;
        }
    
        //查看数组中元素e的索引,若找不到元素e,返回-1
        public int find(int e) {
            for (int i = 0; i < size; i++) {
                if (data[i] == e) {
                    return i;
                }
            }
            return -1;
        }
    
        //删除掉index位置的元素,并且返回删除的元素
        public int remove(int index) {
    
            if (index < 0 || index >= size) {
                throw new IllegalArgumentException("Remove failed. Index is illegal.");
            }
    
            int ret = data[index];
    
            for (int i = index + 1; i < size; i++) {
                data[i - 1] = data[i];
            }
            size--;
            return ret;
        }
    
        //删除掉数组开头的元素,并返回删除的元素
        public int removeFirst() {
            return remove(0);
        }
    
        //删除掉数组末尾的元素,并返回删除的元素
        public int removeLast() {
            return remove(size - 1);
        }
    
        //如果数组中有元素e,那么将其删除,否则什么也不做
        public void removeElement(int e) {
            int index = find(e);
            if (index != -1) {
                remove(index);
            }
        }
    
        @Override
        public String toString() {    //覆盖父类的toString方法
    
            StringBuilder res = new StringBuilder();
            res.append(String.format("Array: size=%d, capacity=%d\n", size, data.length));
            res.append(‘[‘);
            for (int i = 0; i < size; i++) {
                res.append(data[i]);
                if (i != size - 1) {
                    res.append(", ");
                }
            }
            res.append(‘]‘);
            return res.toString();
        }
    }

6.. 现在我们的自定义数组的元素只允许为int类型,我们需要进行优化,让数组可以放置"任意"类型的元素,解决这个问题的技术称之为"泛型"。这里的"任意"加了引号,这是因为在Java中一个泛型类并不能放置任意数据类型的元素,泛型不能放置基本数据类型,只能放置类对象。在Java中,共有8中基本数据类型:int、short、long、boolean、byte、char、float、double。如果将数组设置成泛型数组,那么就无法放置这些基本数据类型了。为了解决这个问题,Java语言为每个基本数据类型都设计了一个包装类,把本来不是类对象的数据包装成了类对象。这8中基本数据类型所对应的包装类分别为:Int、Short、Long、Boolean、Byte、Char、Float、Double,在需要的情况下,包装类与其对应的基本数据类型可以自动进行转换。

7.. 优化后,Array类的业务逻辑如下:

  • public class Array<E> {
    
        private E[] data;  //设置为private,不希望用户从外部直接获取这些信息,防止用户篡改数据
        private int size;
    
        //构造函数,传入数组的容量capacity构造Array
        public Array(int capacity) {
            data = (E[]) new Object[capacity];
            size = 0;
        }
    
        //无参数构造函数,默认数组容量capacity=10
        public Array() {
            this(10);    //这里的capacity是IDE自动添加的提示信息,实际不存在
        }
    
        //获取数组中的元素个数
        public int getSize() {
            return size;
        }
    
        //获取数组的容量
        public int getCapacity() {
            return data.length;
        }
    
        //判断数组是否为空
        public boolean isEmpty() {
            return size == 0;
        }
    
        //向数组末尾添加一个新元素e
        public void addLast(E e) {
            add(size, e);
        }
    
        //向数组开头添加一个新元素e
        public void addFirst(E e) {
            add(0, e);
        }
    
        //在index位置插入一个新元素e
        public void add(int index, E e) {
    
            if (size == data.length) {
                throw new IllegalArgumentException("Add failed. Array is full.");
            }
    
            if (index < 0 || index > size) {
                throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size");
            }
    
            for (int i = size - 1; i >= index; i--) {
                data[i + 1] = data[i];
            }
            data[index] = e;
            size++;
        }
    
        //获取index位置的元素
        public E get(int index) {
            if (index < 0 || index >= size) {
                throw new IllegalArgumentException("Get failed. Index is illegal.");
            }
            return data[index];
        }
    
        //修改index位置的元素为e
        public void set(int index, E e) {
            if (index < 0 || index >= size) {
                throw new IllegalArgumentException("Set failed. Index is illegal.");
            }
            data[index] = e;
        }
    
        //查找数组中是否存在元素e
        public boolean contains(E e) {
            for (int i = 0; i < size; i++) {
                if (data[i].equals(e)) {
                    return true;
                }
            }
            return false;
        }
    
        //查看数组中元素e的索引,若找不到元素e,返回-1
        public int find(E e) {
            for (int i = 0; i < size; i++) {
                if (data[i].equals(e)) {
                    return i;
                }
            }
            return -1;
        }
    
        //删除掉index位置的元素,并且返回删除的元素
        public E remove(int index) {
    
            if (index < 0 || index >= size) {
                throw new IllegalArgumentException("Remove failed. Index is illegal.");
            }
    
            E ret = data[index];
    
            for (int i = index + 1; i < size; i++) {
                data[i - 1] = data[i];
            }
            size--;   //data[size]会指向一个类对象,这部分空间不会被释放loitering objects
            data[size] = null;
            return ret;
        }
    
        //删除掉数组开头的元素,并返回删除的元素
        public E removeFirst() {
            return remove(0);
        }
    
        //删除掉数组末尾的元素,并返回删除的元素
        public E removeLast() {
            return remove(size - 1);
        }
    
        //如果数组中有元素e,那么将其删除,否则什么也不做
        public void removeElement(E e) {
            int index = find(e);
            if (index != -1) {
                remove(index);
            }
        }
    
        @Override
        public String toString() {    //覆盖父类的toString方法
    
            StringBuilder res = new StringBuilder();
            res.append(String.format("Array: size=%d, capacity=%d\n", size, data.length));
            res.append(‘[‘);
            for (int i = 0; i < size; i++) {
                res.append(data[i]);
                if (i != size - 1) {
                    res.append(", ");
                }
            }
            res.append(‘]‘);
            return res.toString();
        }
    }

8.. 再次在main方法中实例化我们的自定义数组,进行测试:

  • public class Main {
    
        public static void main(String[] args) {
    
            Array<Integer> arr = new Array<>(20);
            for (int i = 0; i < 10; i++) {
                arr.addLast(i);
            }
            System.out.println(arr);
    
            arr.add(1, 100);
            System.out.println(arr);
    
            arr.addFirst(-1);
            System.out.println(arr);
    
            arr.remove(2);
            System.out.println(arr);
    
            arr.removeElement(4);
            System.out.println(arr);
    
            arr.removeFirst();
            System.out.println(arr);
    
            arr.removeLast();
            System.out.println(arr);
        }
    }
  • 输出结果如下:
  • Array: size=10, capacity=20
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=11, capacity=20
    [0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=12, capacity=20
    [-1, 0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=11, capacity=20
    [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=10, capacity=20
    [-1, 0, 1, 2, 3, 5, 6, 7, 8, 9]
    Array: size=9, capacity=20
    [0, 1, 2, 3, 5, 6, 7, 8, 9]
    Array: size=8, capacity=20
    [0, 1, 2, 3, 5, 6, 7, 8]

9.. 测试让自定义数组去承载对象

  • public class Student {
    
        private String name;
        private int score;
    
        public Student(String studetName, int studentScore) {
            name = studetName;
            score = studentScore;
        }
    
        @Override
        public String toString() {
            return String.format("Student(name: %s, score: %d)", name, score);
        }
    
        public static void main(String[] args) {
    
            Array<Student> arr = new Array<>();
    
            arr.addLast(new Student("XueZou", 98));
            arr.addLast(new Student("Guiche", 100));
            arr.addLast(new Student("QUiShui", 99));
    
            System.out.println(arr);
        }
    }
  • 输出结果如下:
  • Array: size=3, capacity=10
    [Student(name: XueZou, score: 98), Student(name: Guiche, score: 100), Student(name: QUiShui, score: 99)]

10.. 动态数组,即数组的容量可伸缩

  • 修改add方法的业务逻辑,使自定义数组支持扩容
  • //在index位置插入一个新元素e
    public void add(int index, E e) {
    
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size");
        }
    
        if (size == data.length) {
            resize(2 * size); //扩大为原容量的2倍
        }
    
        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }
        data[index] = e;
        size++;
    }
  • 我们需要实现resize方法,业务逻辑如下:
  • private void resize(int newCapacity) {
        E[] newData = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }
  • 实现扩容后,进行测试:
  • public static void main(String[] args) {
    
        Array<Integer> arr = new Array<>();
        for (int i = 0; i < 10; i++) {
            arr.addLast(i);
        }
        System.out.println(arr);
    
        arr.add(1, 100);
        System.out.println(arr);
    
        arr.addFirst(-1);
        System.out.println(arr);
    }
  • 输出效果如下:
  • Array: size=10, capacity=10
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=11, capacity=20
    [0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=12, capacity=20
    [-1, 0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 修改remove方法,使数组中的元素减少到一定程度时,自动缩小数组容量
  • //删除掉index位置的元素,并且返回删除的元素
    public E remove(int index) {
    
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Remove failed. Index is illegal.");
        }
    
        E ret = data[index];
    
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        size--;   //data[size]会指向一个类对象,这部分空间不会被释放loitering objects
        data[size] = null;
    
        if (size == data.length / 2) {
            resize(data.length / 2);  //被利用的空间等于总空间的一半时,将数组容量减少一半
        }
        return ret;
    }
  • 实现自动降低容量后,进行测试:
  • public static void main(String[] args) {
    
        Array<Integer> arr = new Array<>();
        for (int i = 0; i < 10; i++) {
            arr.addLast(i);
        }
        System.out.println(arr);
    
        arr.add(1, 100);
        System.out.println(arr);
    
        arr.addFirst(-1);
        System.out.println(arr);
    
        arr.remove(2);
        System.out.println(arr);
    
        arr.removeElement(4);
        System.out.println(arr);
    
        arr.removeFirst();
        System.out.println(arr);
    }
  • 输出效果如下:
  • Array: size=10, capacity=10
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=11, capacity=20
    [0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=12, capacity=20
    [-1, 0, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=11, capacity=20
    [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    Array: size=10, capacity=10
    [-1, 0, 1, 2, 3, 5, 6, 7, 8, 9]
    Array: size=9, capacity=10
    [0, 1, 2, 3, 5, 6, 7, 8, 9]

11.. 简单的时间复杂度分析

  • O(1), O(n), O(lgn), O(nlgn), O(n^2)
  • 大O描述的是算法的运行时间和输入数据之间的关系
  • 通常我们会说下面的这段代码的算法是O(n)的,n在这里简单理解为nums中的元素个数,O(n)是说下面这段代码的运行效率,与nums中的元素个数n是呈线性关系的,线性关系体现在n是一次方。
  • public static int sum(int[] nums) {
      int sum = 0;
        for (int num: nums) {
            sum += num;
        return sum;
        }
  • 按照这个思路我们对上面所实现的方法,挨个分析一下它们的算法的时间复杂度:添加操作中addLast(e)方法的时间复杂度是O(1),这表示我们的算法所消耗的时间与我们数据的规模没有关系,这里所指的数据规模是我们数组中的元素个数;addFirst(e)方法的时间复杂度是O(n),因为每个元素都需要向后移动一位;add(index, e)方法的时间复杂度与index的具体取值相关,它的时间复杂度也是O(n);综合来看,添加操作是一个O(n)级别的算法。
  • 删除操作中,removeLast(e)方法的时间复杂度是O(1);removeFirst(e)的时间复杂度是O(n);remove(index, e)的时间复杂度也是O(n);综合来看,删除操作的时间复杂度也是O(n)。
  • 修改操作set(index, e)的时间复杂度是O(1),这是数组的最大优势,专业术语称为"支持随机访问"。
  • 查询操作中,get(index)方法的时间复杂度是O(1);find(e)方法和contains(e)方法的时间复杂度都是O(n)。

12.. 总的来看,对于动态数组来说,增删改查四种操作:

  • 增:时间复杂度O(n);
  • 删:时间复杂度O(n);
  • 改:已知索引O(1),未知索引O(n);
  • 查:已知索引O(1),未知索引O(n);
  • 因此,索引具有语义时,选择数组是非常好的,我们可以通过索引轻松检索数据中的内容。

13.. resize的时间复杂度分析

  • 对于添加操作,如果我们只使用addLast方法,那么它的时间复杂度本来应该是O(1),但是,由于存在resize方法,而resize方法的时间复杂度是O(n),所以addLast方法的时间复杂度就不是O(1)了,但是它的均摊复杂度是O(1);
  • 类似的对于删除操作,如果只使用removeLast方法,那么它的均摊复杂度也是O(1);

14.. 复杂度震荡

  • 复杂度震荡,是指我们在数组容量的临界位置交替进行添加和删除操作,这会导致数组不断执行扩容和缩容操作,而扩容和缩容的时间复杂度都是O(n),出现这种问题的原因在于,我们执行removeLast方法后,resize得有点着急(Eager),解决方案是使用更加懒惰的策略(Lazy),简单理解就是,当数组占用量刚好减到1/2时,不着急缩容,等减到1/4时,再触发缩容,只缩1/2。
  • 我们需要稍微改动一下remove方法,如下所示:
  • public E remove(int index) {
    
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Remove failed. Index is illegal.");
        }
    
        E ret = data[index];
    
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        size--;   //data[size]会指向一个类对象,这部分空间不会被释放loitering objects
        data[size] = null;
    
        if (size == data.length / 4 && data.length / 2 != 0) {       //改动在这里
            resize(data.length / 2);  //被利用的空间等于总空间的一半时,将数组容量减少一半
        }
        return ret;
    }

原文地址:https://www.cnblogs.com/xuezou/p/9276945.html

时间: 2024-10-08 12:08:00

第二十二篇 玩转数据结构——构建动态数组的相关文章

第二十四篇 玩转数据结构——队列(Queue)

1.. 队列基础 队列也是一种线性结构: 相比数组,队列所对应的操作数是队列的子集: 队列只允许从一端(队尾)添加元素,从另一端(队首)取出元素: 队列的形象化描述如下图: 队列是一种先进先出(First In First Out)的数据结构: 2.. 队列的实现 任务目标如下: Queue<E> ·void enqueue(E) //入队 ·E dequeue() //出队 ·E getFront() //查看队首元素 ·int getSize() //查看队列中元素的个数 ·boolean

第二十六篇 玩转数据结构——二分搜索树

1.. 二叉树 跟链表一样,二叉树也是一种动态数据结构,即,不需要在创建时指定大小. 跟链表不同的是,二叉树中的每个节点,除了要存放元素e,它还有两个指向其它节点的引用,分别用Node left和Node right来表示. 类似的,如果每个节点中有3个指向其它节点的引用,就称其为"三叉树"... 二叉树具有唯一的根节点. 二叉树中每个节点最多指向其它的两个节点,我们称这两个节点为"左孩子"和"右孩子",即每个节点最多有两个孩子. 一个孩子都没有

第二十二篇:再写Windows驱动,再玩Windbg---NET

2011年到现在,就没再怎么搞过Windows驱动了. 最近, 由于项目需要, 试着改一改一个显卡驱动(KMDOD), 从实践上证明, 我在理论上对一个驱动的架构的正确与否.(USB Display = KMDOD + AVStream). 其中, KMDOD是完成显示的部分功能, 完成其中的VidPN(Video present network), 将驱动中原来的POST物理设备转变为USB物理设备. 而AVStream之所以这样提出, 完成是由于USB Video class的启发, 要不然

Python开发【第二十二篇】:Web框架之Django【进阶】

Python开发[第二十二篇]:Web框架之Django[进阶] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 新随笔 联系 订阅 管理 随笔-124  文章-127  评论-205 Python之路[第十七篇]:Django[进阶篇 ] Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻

第二十二篇 信念

第二十二篇  信念 "信念"能带给一个人无穷的力量,这些力量可以支撑自己走过漫长的人生.一个人如果没有信念,就很难找到自己的人生方向,所以"信念"也可以理解为希望. 信念可以给到我们希望,也可以给到我们力量,所以一个人的信念会影响到自己的整个人生.当然信念也有好坏之分,好的信念能让自己积极向上.不畏艰难:坏的信念会让我们不思进取.随波逐流.这两种不同的信念会给到我们两种完全不同的人生,就看亲人们如何作出正确的选择. 一个人活在世上,可以选择走正确的人生道路,依靠好的

Egret入门学习日记 --- 第二十二篇(书中 9.7~9.8 节 内容)

第二十二篇(书中 9.7~9.8 节 内容) 开始 9.7节 内容. 重点: 1.进度条ProgressBar的声明和使用. 操作: 1.进度条ProgressBar的声明和使用. 现在真的轻车熟路了,很简单.无非就是设置一下最大值,当前值的属性. 然后,事件监听的话,也是一样的.只不过事件名字的话,我就选书中这个事件吧. 可惜不能发动图,不然你们就可以看到这个进度条,每帧+1的速度前进. 当然,如果你想换自定义皮肤,还是老规矩,去找默认的 EXML 文件. 然后,怎么换素材,就按照自己喜欢的换

第二十二篇 Java的一些关键字、 作用域 还有运算符的使用

大家好,我来和大家一起分享一下今天我所学习到的一些知识点,今天我学会了怎么去使用Java中的一些关键字 .作业域和运算符的使用.... 首先来介绍一下 Java中的关键字有哪些,由于数量有很多 ,我就来说一下我今天所学会的关键字 与其作用: 我学到的第一个关键字是----Scanner ,他在Java中 代表这扫描器的作用,并且要引入包,他的作用就是监听键盘的输入,其中见过次数最多的是在注册或登陆界面上,那是候会直接采用你所输入的值,并取出来使用, 第二个关键字是 是-------return,

python全栈开发基础【第二十二篇】进程池和回调函数

一.数据共享 1.进程间的通信应该尽量避免共享数据的方式 2.进程间的数据是独立的,可以借助队列或管道实现通信,二者都是基于消息传递的. 虽然进程间数据独立,但可以用过Manager实现数据共享,事实上Manager的功能远不止于此. 命令就是一个程序,按回车就会执行(这个只是在windows情况下) tasklist 查看进程 tasklist | findstr pycharm #(findstr是进行过滤的),|就是管道(tasklist执行的内容就放到管道里面了, 管道后面的findst

第二十二篇:信号的接收和处理

前言 要想掌握 Linux 系统编程,自然要好好学学其信号机制. 本文介绍一个简单的信号接收处理程序,为后面继续深入学习信号机制打下基础. 什么是信号 信号是软件中断,它提供一种处理异步事件的方法. 信号产生的条件 1. 当用户按某些终端按键时.比如:Ctrl + D / Ctrl + C 等. 2. 硬件异常.比如:除数为 0,无效内存引用等. 3. 调用 kill 函数可以将信号发送给另一个进程或者进程组. 4. 当检测到某种软件条件已经发生时.比如:alarm 到时,网络传来某些带外数据时