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

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构造Arraypublic Array(int capacity) {data = new int[capacity];size = 0;}//无参数构造函数,默认数组容量capacity=10public 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构造Arraypublic Array(int capacity) {data = new int[capacity];size = 0;}//无参数构造函数,默认数组容量capacity=10public Array() {this(10);    //这里的capacity是IDE自动添加的提示信息,实际不存在
        }//获取数组中的元素个数public int getSize() {return size;}//获取数组的容量public int getCapacity() {return data.length;}//判断数组是否为空public boolean isEmpty() {return size == 0;}//向数组末尾添加一个新元素epublic void addLast(int e) {add(size, e);}//向数组开头添加一个新元素epublic void addFirst(int e) {add(0, e);}//在index位置插入一个新元素epublic 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位置的元素为epublic void set(int index, int e) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Set failed. Index is illegal.");}data[index] = e;}//查找数组中是否存在元素epublic boolean contains(int e) {for (int i = 0; i < size; i++) {if (data[i] == e) {return true;}}return false;}//查看数组中元素e的索引,若找不到元素e,返回-1public 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);}}@Overridepublic 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构造Arraypublic Array(int capacity) {data = (E[]) new Object[capacity];size = 0;}//无参数构造函数,默认数组容量capacity=10public Array() {this(10);    //这里的capacity是IDE自动添加的提示信息,实际不存在
        }//获取数组中的元素个数public int getSize() {return size;}//获取数组的容量public int getCapacity() {return data.length;}//判断数组是否为空public boolean isEmpty() {return size == 0;}//向数组末尾添加一个新元素epublic void addLast(E e) {add(size, e);}//向数组开头添加一个新元素epublic void addFirst(E e) {add(0, e);}//在index位置插入一个新元素epublic 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位置的元素为epublic void set(int index, E e) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Set failed. Index is illegal.");}data[index] = e;}//查找数组中是否存在元素epublic boolean contains(E e) {for (int i = 0; i < size; i++) {if (data[i].equals(e)) {return true;}}return false;}//查看数组中元素e的索引,若找不到元素e,返回-1public 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 objectsdata[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);}}@Overridepublic 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;}@Overridepublic 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 objectsdata[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 objectsdata[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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/266724.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言函数日记

参考&#xff1a;C语言-函数-日记 作者&#xff1a;9art0 发布时间&#xff1a;2020-08-31 00:57:06 网址&#xff1a;https://blog.csdn.net/GatoWong/article/details/108314011?spm1001.2014.3001.5501 C语言-函数-日记2.1.函数概述一2.2.函数概述二2.3.函数无返回参数&…

python运行调出控制台_python.exe 和 pythonw.exe 的区别

(区分.py、.pyw、.pyc 文件)最近也在学习python&#xff0c;针对python中的一些问题做下记录&#xff0c;希望大家共同成长&#xff1b; Windows系统搭建好Python的环境后&#xff0c;进入Python的安装目录&#xff0c;大家会发现目录中有python.exe和pythonw.exe两个程序。 …

Vim的6种模式

Vim的6种模式|微刊 - 悦读喜欢Vim的6种模式大多数人都知道 Vim的两种模式, 正常模式和 插入模式,实际上 Vim有六种模式.- 正常模式 (Normal mode): 用户输入命令的模式- 可视化模式 (Visual mode): 可视化区块被高亮的时候- 选择模式 (Select mode): 类似可视模式 但是用用户输…

修复计算机u盘,u盘损坏怎么修复 u盘损坏修复方法

造成u盘损坏的原因有很多&#xff0c;比如我们在拔出u盘前&#xff0c;如果没有先将u盘弹出或者弹出的不彻底&#xff0c;就很可能造成u盘的损坏。u盘损坏就会导致计算机无法识别出u盘。那u盘损坏了能不能修复?如果能修复&#xff0c;那要怎么操作?下面就让我们一起来看看u盘…

.net 技术类网址

本人在工作学习中遇到的一些技术的网站&#xff0c;讲的也很详细&#xff0c;与各位共享&#xff0c;希望对您有帮助EF实体类&#xff1a; https://msdn.microsoft.com/zh-cn/library/gg696172(vvs.103).aspx数据库&#xff1a; https://docs.microsoft.com/zh-cn/sql/ssms/dow…

433M射频灯

参考&#xff1a;433M射频灯 作者&#xff1a;9art0 发布时间&#xff1a;2020-09-21 21:57:29 网址&#xff1a;https://blog.csdn.net/GatoWong/article/details/108700989?spm1001.2014.3001.5501 目录433M射频灯硬件接线1. KEIL建立工程2. 电子可编程开关继电器原理3. 上电…

2清空所有表_拉链表(二)

拉链表&#xff08;一&#xff09;拉链表&#xff08;二&#xff09;一、前言在上一节简单介绍了拉链表&#xff0c;本节主要讲解如何通过binlog采集MySQL的数据并且按月分区的方式实现拉链表。这里以上节介绍的用户表(user) 举例二、涉及到的表1. 原始表&#xff08;user&…

出生日期范围的Sql语句_【呕心总结】python如何与mysql实现交互及常用sql语句

9 月初&#xff0c;我对 python 爬虫 燃起兴趣&#xff0c;但爬取到的数据多通道实时同步读写用文件并不方便&#xff0c;于是开始用起mysql。这篇笔记&#xff0c;我将整理近一个月的实战中最常用到的 mysql 语句&#xff0c;同时也将涉及到如何在python3中与 mysql 实现数据交…

iphone的生命周期

ios应用的生命周期在开发过程中我们需要一些全局对象来将程序的各个部分连接起来&#xff0c;这些全局对象中最重要的就是UIApplication对象。但在实际编程中我们并不直接和UIApplication对象打交道&#xff0c;而是和其代理打交道。UIApplication 是iPhone应用程序的开始并且负…

vb发送邮箱连接服务器失败,VB:如何用需要身份验证的SMTP邮件服务器发信

Option Explicit需要引用 Microsoft CDO for Windows 2000 Library和 Microsoft ActiveX Data Objects 2.5 LibraryDim objConfig As CDO.ConfigurationDim objMessage As CDO.MessageDim Fields As ADODB.Fields Get a handle on the config object and its fieldsSe…

【ACM】最少乘法次数 - 树

最少乘法次数 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;3描述给你一个非零整数&#xff0c;让你求这个数的n次方&#xff0c;每次相乘的结果可以在后面使用&#xff0c;求至少需要多少次乘。如24&#xff1a;2*222&#xff08;第一次乘&a…

基于Wemos的智能感应开盖垃圾桶——日记

参考&#xff1a;基于Wemos的智能感应开盖垃圾桶——日记 作者&#xff1a;9art0 发布时间&#xff1a;2020-09-24 00:40:59 网址&#xff1a;https://blog.csdn.net/GatoWong/article/details/108759063?spm1001.2014.3001.5501 1.项目软硬件平台及开发环境 1.1.硬件平台 1…

严格程度_国内医疗器械生产许可证申请申报-经营一类、二类、严格的三类之间区别...

医疗器械生产许可证是医疗器械生产企业必须持有的证件&#xff0c;由当地药监局审核颁发。开办医疗器械生产企业应当符合国家医疗器械行业发展规划和产业政策。医疗器械生产许可证同样也是根据生产医疗器械的种类不同&#xff0c;需要办理资质审批也是不同的&#xff0c;具体如…

matlab z变换离散化_用C++编写一个简单的光栅化渲染器:3D篇

3D光栅化与2D光栅化在图元绘制方面差别并不大&#xff0c;3D光栅化主要是多了很多坐标系(Local&#xff0c;world&#xff0c;View...)&#xff0c;除此外遮挡算法和裁剪算法也会稍微复杂一些。本篇文章的重点就主要集中在各种坐标系变换上。1.基本3D变换本文所采用的向量(vect…

Catalan数列

引入 今天听学长讲了卡特兰数列后对其有了更深的认识&#xff0c;在此完善了一下之前的博客加以总结。 首先用一个经典的例子来描述一下Catalan数列&#xff0c;我们有一个1~n的数列和一个大小为n的栈&#xff0c;我们有如下两种操作&#xff1a; 当未入栈序列不为空时&#xf…

计算机和外部通信方式,计算机和外部的通信方式

计算机和外部交换信息又称为通信(Communication)。按数据传送方式分为并行通信和串行通信两种基本方式。1、并行通信并行通信就是把传送数据的n位数用n条传输线同时传送。其优点是传送速度快、信息率高。并且&#xff0c;通常只要提供二条控制和状态线&#xff0c;就能完成CPU和…

android jni 结构体_Android——- jni返回结构体-Fun言

1.创建工程&#xff0c;添加jni2. main.cpp#include struct test_data{char name[256];int num;double average;};extern "C" {JNIEXPORT jobject JNICALL Java_com_re_struct_MainActivity_call(JNIEnv * env, jobject obj, jobject classobj);};JNIEXPORT jobject …

stm32入门之keil5的安装以及第一个工程的建立

摘自&#xff1a;stm32入门之keil5的安装以及第一个工程的建立 作者&#xff1a;SKY丶丿平才 发布时间&#xff1a; 2020-12-06 17:08:30 网址&#xff1a;https://blog.csdn.net/weixin_48264057/article/details/110734596 文章目录 前言一、keil5的下载与安装1.下载相关链接…

Java的静态域以及封装性相关

EmployeeTest类&#xff0c;用于定义一个简单的属性类。 1 package yang.src;2 3 import java.util.Date;4 5 public class EmployeeTest {6 private Date hireDay;7 public static final int gId 10;8 9 public int iData 11; 10 public static int iData1 …

微信电脑客户端_无聊的话,用微信玩玩电脑

被禁足的2020注定是让人印象深刻的一年如果现在你很无聊来试试用微信玩玩电脑看能否给这不出门就是做贡献的日子带来一点乐趣开启步骤1、在能使用(接触的)的电脑上安装上Hipc在电脑访问 hipc.cn下载PC客户端(仅2M左右大小)2、安装PC客户端3、微信扫一扫绑定现在看看如何用微信玩…