动态扩容数组

  • 什么是数据结构?
  • 线性表
  • 数组(Array)
  • 动态数组(Dynamic Array)
    • 动态数组接口设计
    • 清除所有元素 - clear()
    • 添加元素 - add(E element)、add(int index, E element)
    • 删除元素 - remove(int index)、清空数组 - clear()
    • 是否包含某个元素 - contains(E element)
    • 扩容 - ensureCapacity(int capacity)
    • 打印数组(toString)
  • 泛型
    • 对象数组 - Object[]
  • int型动态数组源码(Java)
  • 泛型动态数组源码(Java)
  • 泛型动态数组源码(C++)

数据结构与算法笔记目录:《恋上数据结构》 笔记目录

本章源码:恋上数据结构源码 - 动态数组

什么是数据结构?

数据结构是计算机存储、组织数据的方式;

在实际应用中,根据使用场景来选择最合适的数据结构

线性表

线性表是具有 n 个相同类型元素的有限序列( n ≥ 0 )

  • a1 是首节点(首元素), an 是尾结点(尾元素)
  • a1 是 a2前驱, a2 是 a1后继

常见的线性表有:

  • 数组
  • 链表
  • 队列
  • 哈希表(散列表)

数组(Array)

数组是一种顺序存储的线性表,所有元素的内存地址是连续

在很多编程语言中,数组都有个致命的缺点: 无法动态修改容量

实际开发中,我们更希望数组的容量是可以动态改变的

动态数组(Dynamic Array)

动态数组接口设计


动态数组的结构:

  • size
  • elements

Java 中,成员变量会自动初始化,比如:

  • int 类型自动初始化为 0
  • 对象类型自动初始化为 null

清除所有元素 - clear()

核心代码是 size = 0,实际上不需要执行 elements = null,size = 0 的操作已经对用户来说保证了无法访问动态数组中的元素了。

添加元素 - add(E element)、add(int index, E element)

add(E element):默认往数组最后添加元素;

add(int index, E element):在 index 位置插入一个元素;

比如要往 index = 2 的地方添加元素:

  • 正确的顺序应当是:从后往前开始将 index = 2 以后的元素依次后移,然后赋值:
  • 如果从前往后开始移动元素,会造成如下错误的后果:

代码实现:

/*** 在index位置插入一个元素* @param index* @param element*/
public void add(int index, E element){ rangeCheckForAdd(index); // 检查下标越界ensureCapacity(size + 1); // 确保容量够大// 0 1 2 3 4 5 6 7 8 9 (index)// 1 2 3 4 5 6 x x x x   (原数组)// 在index=2处,插入9,元素全部后移// 1 2 9 3 4 5 6 x x x   (add后数组)// 先从后往前开始, 将每个元素往后移一位, 然后再赋值for (int i = size - 1; i > index; i--) {elements[i + 1] = elements[i];}elements[index] = element; // 复制size++;
}
/*** 添加元素到数组最后*/
public void add(E element){add(size, element);
}

删除元素 - remove(int index)、清空数组 - clear()

例如,删除 index = 3 的数组元素,应当从前往后开始移动,用后面的元素覆盖前面的元素。

思考最后一个元素如何处理

  • 如果存放 int 类型,size-- 后,最后一个元素已经无法访问了。
  • 如果使用泛型,数组要注意内存管理(将元素置 null)。
    使用泛型技术可以让动态数组更加通用,可以存放任何数据类型:

代码实现:

/*** 删除index位置的元素* @param index* @return*/
public E remove(int index){rangeCheck(index);// 0 1 2 3 4 5     (index)// 1 2 3 4 5 6   (原数组)// 删除index为2的元素,元素前移// 1 2 4 5 6    (remove后的数组)// 从前往后开始移, 用后面的元素覆盖前面的元素E old = elements[index];for (int i = index; i < size - 1; i++) {elements[i] = elements[i + 1];}// 下面是使用泛型后需要写的(如果存储的是int型数据, 则不需要)elements[--size] = null; // 删除元素后, 将最后一位设置为nullreturn old;
}
/*** 清除所有元素*/
public void clear(){// 使用泛型数组后要注意内存管理(将元素置null)for (int i = 0; i < size; i++) {elements[i] = null;}size = 0;
}

是否包含某个元素 - contains(E element)

关于 null 的处理主要看你的业务需求:是否可以存储 null 数据?

/*** 是否包含某个元素* @param element* @return*/
public boolean contains(E element){return indexOf(element) != ELEMENT_NOT_FOUND; // 找的到该元素则返回True
}
/**
* 查看元素的索引* @param element* @return*/
public int indexOf(E element){/*// 不对 null 进行处理也可以,但是健壮性不够for (int i = 0; i < size; i++) {if(elements[i].equals(element)) return i;}*/if(element == null){ // 对 null 进行处理for (int i = 0; i < size; i++) {if(elements[i] == null) return i;}}else{for (int i = 0; i < size; i++) {if(elements[i].equals(element)) return i;}}return ELEMENT_NOT_FOUND;
}

扩容 - ensureCapacity(int capacity)

相信看过视频的小伙伴一看这图就懂了。

扩容操作代码实现:

/*** 扩容操作*/
private void ensureCapacity(int capacity){int oldCapacity = elements.length;if (oldCapacity >= capacity) return;// 新容量为旧容量的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);E[] newElements = (E[])new Object[newCapacity];for (int i = 0; i < size; i++) {newElements[i] = elements[i]; // 拷贝原数组元素到新数组}elements = newElements;System.out.println("size="+oldCapacity+", 扩容到了"+newCapacity);
}

打印数组(toString)

  • 重写 toString 方法
  • toString 方法中将元素拼接成字符串
  • 字符串拼接建议使用 StringBuilder
@Override
public String toString() {// 打印形式为: size=5, [99, 88, 77, 66, 55]StringBuilder string = new StringBuilder();string.append("size=").append(size).append(", [");for (int i = 0; i < size; i++) {if(0 != i) string.append(", ");string.append(elements[i]);}string.append("]");return string.toString();
}

泛型

使用泛型技术可以让动态数组更加通用,可以存放任何数据类型

public class ArrayList<E> {private int size;private E[] elements;
}
elements = (E[]) new Object[capacity];
ArrayList<Integer> list = new ArrayList<>();

对象数组 - Object[]

Object[] objects = new Object[7];
  • 由于 Object 可以存放任何类型,因此无法固定 Object 占多少内存空间(根据传入不同的自定义对象,占用的空间都不同),因此实际上 Object 数组中存放的是对象的地址
  • 想要销毁对象,只需要将指向该对象的地址赋值为 null,没有地址引用该对象,则会自动被垃圾回收

内存管理细节:

public void clear() {for (int i = 0; i < size; i++) {elements[i] = null; // 管理内存}size = 0;
}
public E remove(int index) {rangeCheck(index);E oldElement = elements[index];for (int i = index; i < size - 1; i++) {elements[i] = elements[i + 1];}elements[--size] = null; // 管理内存return oldElement;
}

int型动态数组源码(Java)

public class ArrayList {private int size;        // 元素数量 private int[] elements; // 所有的元素private static final int DEFAULT_CAPACITY = 10; // 初始容量private static final int ELEMENT_NOT_FOUND = -1;public ArrayList(int capacity) { // 容量小于10一律扩充为10capacity = (capacity < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capacity;elements = new int[capacity];}public ArrayList(){this(DEFAULT_CAPACITY);}/*** 元素的数量* @return*/public int size(){return size;}/*** 是否为空* @return*/public boolean isEmpty(){return size == 0;}/*** 是否包含某个元素* @param element* @return*/public boolean contains(int element){return indexOf(element) != ELEMENT_NOT_FOUND; //找的到该元素则返回True}/*** 在index位置插入一个元素* @param index* @param element*/public void add(int index, int element){rangeCheckForAdd(index); // 检查下标越界ensureCapacity(size + 1); // 确保容量够大// 0 1 2 3 4 5 6 7 8 9   (index)// 1 2 3 4 5 6 x x x x   (原数组)// 在index=2处,插入9,元素全部后移// 1 2 9 3 4 5 6 x x x   (add后数组)// 先从后往前开始, 将每个元素往后移一位, 然后再赋值for (int i = size - 1; i > index; i--) {elements[i + 1] = elements[i];}elements[index] = element; // 赋值size++;}/*** 添加元素到最后面*/public void add(int element){add(size, element);}/*** 设置index位置的元素* @param index* @param element* @return 原来的元素ֵ*/public int get(int index){rangeCheck(index);return elements[index];}/*** 设置index位置的元素* @param index* @param element* @return 原来的元素ֵ*/public int set(int index, int element){rangeCheck(index);int old = elements[index];elements[index] = element;return old;}/*** 删除index位置的元素* @param index* @return*/public int remove(int index){rangeCheck(index);// 0 1 2 3 4 5   (index)// 1 2 3 4 5 6   (原数组)// 删除index为2的元素,元素前移// 1 2 4 5 6    (remove后的数组)int old = elements[index];// 从前往后开始移, 用后面的元素覆盖前面的元素for (int i = index; i < size-1; i++) {elements[i] = elements[i + 1];}size--;return old;}/*** 查看元素的索引* @param element* @return*/public int indexOf(int element){for (int i = 0; i < size; i++) {if(elements[i] == element) return i;}return ELEMENT_NOT_FOUND;}/*** 清除所有元素*/public void clear(){size = 0;}/** 扩容操作*/private void ensureCapacity(int capacity){int oldCapacity = elements.length;if(oldCapacity >= capacity) return;// 新容量为旧容量的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5int[] newElements = new int[newCapacity];for (int i = 0; i < size; i++) {newElements[i] = elements[i];}elements = newElements;System.out.println("size="+oldCapacity+", 扩容到了"+newCapacity);}/****************封装好的功能函数*******************************/// 下标越界抛出的异常private void outOfBounds(int index) {throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);}// 检查下标越界(不可访问或删除size位置)private void rangeCheck(int index){if(index < 0 || index >= size){outOfBounds(index);}}// 检查add()的下标越界(可以在size位置添加)private void rangeCheckForAdd(int index) {if (index < 0 || index > size) {outOfBounds(index);}}/****************封装好的功能函数*******************************/@Overridepublic String toString() {// 打印形式为: size=5, [99, 88, 77, 66, 55]StringBuilder string = new StringBuilder();string.append("size=").append(size).append(", [");for (int i = 0; i < size; i++) {if(0 != i) string.append(", ");string.append(elements[i]);}string.append("]");return string.toString();}
}

泛型动态数组源码(Java)

@SuppressWarnings("unchecked")
public class ArrayList<E> {private int size;      // 元素的数量    private E[] elements;   // 所有的元素private static final int DEFAULT_CAPACITY = 10; // 初始容量private static final int ELEMENT_NOT_FOUND = -1;public ArrayList(int capacity) { // 容量小于10一律扩充为10capacity = (capacity < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capacity;elements = (E[])new Object[capacity];}public ArrayList(){this(DEFAULT_CAPACITY);}/*** 元素的数量* @return*/public int size(){return size;}/*** 是否为空* @return*/public boolean isEmpty(){return size == 0;}/*** 是否包含某个元素* @param element* @return*/public boolean contains(E element){return indexOf(element) != ELEMENT_NOT_FOUND; // 找的到该元素则返回True}/*** 在index位置插入一个元素* @param index* @param element*/public void add(int index, E element){ rangeCheckForAdd(index); // 检查下标越界ensureCapacity(size + 1); // 确保容量够大// 0 1 2 3 4 5 6 7 8 9 (index)// 1 2 3 4 5 6 x x x x   (原数组)// 在index=2处,插入9,元素全部后移// 1 2 9 3 4 5 6 x x x   (add后数组)// 先从后往前开始, 将每个元素往后移一位, 然后再赋值for (int i = size - 1; i > index; i--) {elements[i + 1] = elements[i];}elements[index] = element; // 复制size++;}/*** 添加元素到最后面*/public void add(E element){add(size, element);}/*** 设置index位置的元素* @param index* @param element* @return 原来的元素ֵ*/public E get(int index){rangeCheck(index);return elements[index];}/*** 设置index位置的元素* @param index* @param element* @return 原来的元素ֵ*/public E set(int index, E element){rangeCheck(index);E old = elements[index];elements[index] = element;return old;}/*** 删除index位置的元素* @param index* @return*/public E remove(int index){rangeCheck(index);// 0 1 2 3 4 5   (index)// 1 2 3 4 5 6   (原数组)// 删除index为2的元素,元素前移// 1 2 4 5 6    (remove后的数组)// 从前往后开始移, 用后面的元素覆盖前面的元素E old = elements[index];for (int i = index; i < size - 1; i++) {elements[i] = elements[i + 1];}elements[--size] = null; // 删除元素后, 将最后一位设置为nullreturn old;}/*** 查看元素的索引* @param element* @return*/public int indexOf(E element){/*// 不对 null 进行处理也可以,但是健壮性不够for (int i = 0; i < size; i++) {if(elements[i].equals(element)) return i;}*/if(element == null){ // 对 null 进行处理for (int i = 0; i < size; i++) {if(elements[i] == null) return i;}}else{for (int i = 0; i < size; i++) {if(elements[i].equals(element)) return i;}}return ELEMENT_NOT_FOUND;}/*** 清除所有元素*/public void clear(){// 使用泛型数组后要注意内存管理(将元素置null)for (int i = 0; i < size; i++) {elements[i] = null;}size = 0;}/*** 扩容操作*/private void ensureCapacity(int capacity){int oldCapacity = elements.length;if(oldCapacity >= capacity) return;// 新容量为旧容量的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);E[] newElements = (E[])new Object[newCapacity];for (int i = 0; i < size; i++) {newElements[i] = elements[i]; // 拷贝原数组元素到新数组}elements = newElements;System.out.println("size="+oldCapacity+", 扩容到了"+newCapacity);}/****************封装好的功能函数**************************/// 下标越界抛出的异常private void outOfBounds(int index) {throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);}// 检查下标越界(不可访问或删除size位置)private void rangeCheck(int index){if(index < 0 || index >= size){outOfBounds(index);}}// 检查add()的下标越界(可以在size位置添加元素)private void rangeCheckForAdd(int index) {if (index < 0 || index > size) {outOfBounds(index);}}/****************封装好的功能函数***************************/@Overridepublic String toString() {// 打印形式为: size=5, [99, 88, 77, 66, 55]StringBuilder string = new StringBuilder();string.append("size=").append(size).append(", [");for (int i = 0; i < size; i++) {if(0 != i) string.append(", ");string.append(elements[i]);}string.append("]");return string.toString();}
}

测试运行:

public static void main(String[] args) {ArrayList<Person> list = new ArrayList<>();list.add(new Person(10, "jack"));list.add(new Person(20, "rose"));list.add(null);list.add(null);System.out.println("add()添加元素: " + list);System.out.println("get()获取元素: " + list.get(0));list.set(0, new Person(99, "ghost"));System.out.println("set()设置元素值: " + list);list.remove(0);System.out.println("remove()删除元素: " + list);list.clear();System.out.println("clear()清空数组: " + list);
}
add()添加元素: size=4, [Person [age=10, name=jack], Person [age=20, name=rose], null, null]
get()获取元素: Person [age=10, name=jack]
set()设置元素值: size=4, [Person [age=99, name=ghost], Person [age=20, name=rose], null, null]
remove()删除元素: size=3, [Person [age=20, name=rose], null, null]
clear()清空数组: size=0, []

泛型动态数组源码(C++)

一时兴起写了个C++版本的动态数组。。。如果可以的话,希望后面的数据结构都可以用C++自己实现一遍。。(目前只写了这个)

#include<iostream>
using namespace std;
#define ELEMENT_NOT_FOUND -1;template<typename E>
class Array
{private:int m_size;     // 元素数量int m_capacity; // 数组容量E * m_elements;   // 指向首地址void outOfBounds(int index) {throw index;}void rangeCheck(int index);   // 检查下标(get,set)void rangeCheckForAdd(int index); // 检查下标(add)void ensureCapacity(int capacity); // 检查容量及扩容
public:Array(int capacity = 10);~Array();int size(); // 元素的数量bool isEmpty(); // 是否为空int indexOf(E element); // 查看元素的位置bool contains(E element); // 是否包含某个元素E set(int index, E element); // 设置index位置的元素E get(int index); // 返回index位置对应的元素void add(int index, E element); // 往index位置添加元素void add(E element); // 添加元素到最后面E remove(int index); // 删除index位置对应的元素void clear(); // 清除所有元素
};
template<typename E>
void Array<E>::rangeCheck(int index) {if (index < 0 || index >= m_size)outOfBounds(index);
}
template<typename E>
void Array<E>::rangeCheckForAdd(int index) {if(index < 0 || index > m_size)outOfBounds(index);
}
template<typename E>
void Array<E>::ensureCapacity(int capacity) {int oldCapacity = m_capacity;if (oldCapacity >= capacity) return;// 新容量为旧容量的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);E *newElements = new E[newCapacity];for (int i = 0; i < m_size; i++) {newElements[i] = m_elements[i];}delete[] m_elements; // 释放原空间m_elements = newElements;m_capacity = newCapacity;cout << oldCapacity << "扩容为" << newCapacity << endl;
}template<typename E>
Array<E>::Array(int capacity) {m_capacity = (capacity < 10) ? 10 : capacity;m_elements = new E[m_capacity];
}
template<typename E>
Array<E>::~Array() {delete[] m_elements;
}
template<typename E>
int Array<E>::size(){return m_size;
}
template<typename T>
bool Array<T>::isEmpty() {return m_size == 0;
}
template<typename E>
int Array<E>::indexOf(E element) {for (int i = 0; i < m_size; i++) {if (m_elements[i] == element) return i;}return ELEMENT_NOT_FOUND;
}
template<typename E>
bool Array<E>::contains(E element) {return indexOf(element) != ELEMENT_NOT_FOUND;
}
template<typename E>
E Array<E>::set(int index, E element) {rangeCheck(index);E old = element;m_elements[index] = element;return old;
}
template<typename E>
E Array<E>::get(int index) {rangeCheck(index);return m_elements[index];
}
template<typename E>
void Array<E>::add(int index, E element) {rangeCheckForAdd(index);ensureCapacity(m_size + 1);// 0 1 2 3 4 5 // 1 2 3 5 6 7// index=3, element=4for (int i = m_size; i > index; i--) {m_elements[i] = m_elements[i-1];}m_elements[index] = element;m_size++;
}
template<typename E>
void Array<E>::add(E element) {add(m_size, element);
}
template<typename E>
E Array<E>::remove(int index) {rangeCheck(index);E old = m_elements[index];// 0 1 2 3 4 5 // 1 2 3 5 6 7// index=2for (int i = index; i < m_size; i++) {m_elements[i] = m_elements[i + 1];}m_elements[--m_size] = NULL;return old;
}
template<typename E>
void Array<E>::clear() {// m_elements = nullptr; // 不可行,直接清除了整个指针指向的地址for (int i = 0; i < m_size; i++) {m_elements[i] = NULL;}m_size = 0;
}int main() {Array<int> array;for (int i = 0; i < 30; i++) {array.add(i);}cout << "array.set(0, 99): " << array.set(0, 99) << endl;cout << "array.remove(0): " << array.remove(0) << endl;cout << "array.isEmpty(): " << array.isEmpty()<< endl;cout << "array.cotains(5): " << array.contains(5) << endl;cout << "size = " << array.size() << endl;array.add(10, 99); cout << "array.add(10, 99), size = " << array.size() << endl;for (int i = 0; i < array.size() ;  i++) {if (i != 0) {cout << ", ";}cout << array.get(i);}
}
10扩容为15
15扩容为22
22扩容为33
array.set(0, 99): 99
array.remove(0): 99
array.isEmpty(): 0
array.cotains(5): 1
size = 29
array.add(10, 99), size = 30
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 99, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29

《恋上数据结构第1季》动态扩容数组原理及实现相关推荐

  1. 《恋上数据结构第1季》动态数组实现栈

    栈(Stack) 栈的应用 – 浏览器的前进和后退 栈的接口设计 动态数组实现栈 练习题 逆波兰表达式求值 有效的括号 数据结构与算法笔记目录:<恋上数据结构> 笔记目录 想加深 Java ...

  2. 《恋上数据结构第1季》二叉搜索树BST

    二叉搜索树(BinarySearchTree) BST 接口设计 BST 基础 添加元素: add() 删除元素: remove() 删除节点 – 叶子节点 删除节点 – 度为1的节点 删除节点 – ...

  3. 《恋上数据结构第1季》队列、双端队列、循环队列、循环双端队列

    队列(Queue) 队列 Queue 队列的接口设计 队列源码 双端队列 Deque 双端队列接口设计 双端队列源码 循环队列 Circle Queue 循环队列实现 索引映射封装 循环队列 – %运 ...

  4. 《恋上数据结构第1季》二叉堆原理及实现、最小堆解决 TOP K 问题

    二叉堆 BinaryHeap 堆(Heap) 堆的出现 堆简介 二叉堆(Binary Heap) 获取最大值 最大堆 - 添加 最大堆 - 添加优化 最大堆 - 删除 replace 最大堆 - 批量 ...

  5. 《恋上数据结构第1季》集合 ListSet、TreeSet、HashSet

    集合(Set) 集合的接口定义 双向链表 LinkedList 实现 ListSet 红黑树 RBTree 实现 TreeSet TreeMap 实现 TreeSet HashMap 实现 HashS ...

  6. 《恋上数据结构第1季》单向循环链表、双向循环链表以及约瑟夫环问题

    循环链表(CircleList) 链表的接口设计 单向循环链表 单向循环链表完整源码 双向循环链表 双向循环链表完整源码 双向循环链表解决约瑟夫环问题 如何发挥循环链表的最大威力? 静态链表 数据结构 ...

  7. 《恋上数据结构第1季》单向链表、双向链表

    链表(Linked List) 链表的接口设计 单向链表(SingleLinkedList) 获取元素 – get() 清空元素 – clear() 添加元素 – add(int index, E e ...

  8. 《恋上数据结构第1季》B树

    B树 m阶B树的性质 B树 vs 二叉搜索树 搜索 添加 – 上溢 添加 – 上溢的解决(假设5阶) 删除 删除 – 叶子节点 删除 – 非叶子节点 删除 – 下溢 删除 – 下溢的解决 4阶B树 数 ...

  9. 《恋上数据结构第1季》二叉树代码实现

    二叉树(BinaryTree) BinaryTree 基础 遍历(先序.中序.后序.层次遍历) 先序遍历: preorder() 中序遍历: inorder() 后序遍历: postorder() 层 ...

最新文章

  1. 查询数据库返回cursor,如何判断没有符合条件的记录。判断条件不是cursor==null 哦
  2. 生成QR二维码的多种方法
  3. 是是非非本寻常,我们要不要跳槽。
  4. mysql数据库备份出错_mysql数据库备份成功,再还原却失败,什么原
  5. CNN tensorflow 人脸识别
  6. php 条形码生成器,PHP条形码图像生成器
  7. webpack 引入css link,demo11 webpack处理css
  8. linux下java多线程_Linux系统下Java问题排查——cpu使用率过高或多线程锁问题
  9. 动感英语笔记_小红书奇葩笔记大赏
  10. HTML网页作业个人网站源码div+css布局
  11. 谷歌浏览器html插件字体大小,字体换加插件,自定义Chrome网页字体,更改字体样式,调整字体大小...
  12. Word插入页码简单方法
  13. 图片的灰度处理(Unity Shader)
  14. 古诗词在线起名 - 一刀工具箱
  15. 网络系统实现技术--AppleTalk
  16. 自媒体平台图文创作建议和技巧
  17. mmdet更改faster-rcnn的主干网络训练
  18. 牛逼啦!20行Python代码让程序自动玩“合成大西瓜”!
  19. SQL优化(面试题)
  20. printf和println和print区别

热门文章

  1. 聪明女人的七个致富之道,学会它你也能成为人人羡慕的小富婆
  2. 很多创业失败的负债者
  3. 口语技巧-Part2答题逻辑
  4. JDK和IDEA的安装(JDK11及以上版本自带JRE)
  5. Linux 高级流量控制
  6. 批量关停azure vm_创建Azure自动化以启动Azure VM
  7. 什么是SQL Server事务日志中的虚拟日志文件?
  8. 人工智能及其体系结构_一些复制体系结构错误及其解决方案
  9. SQL Server数据库迁移–将数据库克隆到另一个排序规则
  10. 【剑指Offer】29、最小的K个数