顺序表
顺序表不仅在逻辑上上是连续的,并且物理地址上连续的,就像下面的图一样,每一块物理地址都是紧紧挨着的,且存在线性关系
因此实际取值是不需要一个一个查找,可以直接通过索引计算偏移值,直接获取到对应索引的物理地址
对应索引地址 = 循序表相对偏移地址 + 单个元素大小 * 偏移个数
例如:数组的第一项地址为 10 ,单个元素大小为2, 那么要想找到索引为4的地址,直接计算结果 10 + 2 * 4 = 18 即可
顺序表的赋值
从上面得出循序表赋值的话,只能在提前申请好的内存下给对应地址赋值即可,例如:
int a[20] = {0};
a[1] = 2;
顺序表的插入
如果是想按照顺序插入,那么插入元素会被覆盖,因此只能先从插入节点一次向后移动一位,然后插入到固定位置即可
int a[20] = {0};
//插入2到索引3
for (int i = 3; i < 19; i++) {
a[i+1] = a[i];
}
a[3] = 2;
可是会发现一个漏洞,如果上面都有值了,最后一个元素不是会没有么,因此实际插入,会申请一个比原来还大一个空间的新数组,将老数组copy进去在插入即可
int a[20] = {0};
int newA[21] = {0};
int i = 0;
while (i < 20) {
newA[i] = a[i];
i++;
}
//然后替换即可
for (int i = 3; i < 19; i++) {
a[i+1] = a[i];
}
a[3] = 2;
看了上面可能会觉得性能浪费,那么可以在copy的时候就处理好插入问题哈,这里就不多介绍了
顺序表的删除
顺序表的删除同插入一样,直接将后面的元素向前移动覆盖一位,然后将最后一位换成 0 即可
int a[20] = {0};
//删除索引3
int i = 3;
for (i = 3; i < 19; i++) {
a[i] = a[i+1];
}
//然后最后一位置空
a[i] = 0;
这样就完成删除了
链表
链表与顺序表不同,链表保证逻辑上是连续的即可,物理地址是非连续的,是以指针的形式对元素之间进行关联,如下图所示
各个节点之间是由一个一个的连续指向关系行程的一个有序的数组,因此他们的逻辑上是连续的,但物理地址是可以不连续,毕竟可以通过前面的某个节点来获取对应索引的节点
对比顺序表的每次插入和删除都需要大量移动元素可以看出链表的插入和删除只需要重新建立关系就可以了,因此:链表插入删除要比顺序表简单
链表的查找
链表的查找相对循序表要慢了许多,由于地址的不连续性,只能一个一个向后查找,下面查找索引为 3 的元素
LinkList head; //head表示第一个
//查找索引为3的元素
int i = 0;
LinkList *node = head;
white (i < 3 && node) {
node = node->next;
i++;
}
//此时node就是第3个元素了
链表的插入
需要通过查找方法找到当前插入索引的前一个节点(索引为2的节点)preNode,然后将新节点newNode的下一个指针next,指向preNode的下一个节点next,最后preNode的next指向newNode即可
LinkList preNode; //通过查找方法找到的要插入位置的前一个节点,即2
LinkList newNode;//要插入的新数据节点
//查找索引为3的元素
newNode->next = preNode->next;
preNode->next = newNode;
这样便完成了插入操作
链表的删除
类似插入,只需要将指定节点的前一个节点preNode的next,指向删除节点deleteNode的下一个节点next,然后删除deleteNode即可
LinkList preNode; //通过查找方法找到的要插入位置的前一个节点,即2
LinkList deleteNode = preNode->next;//要删除的节点
//查找索引为3的元素
preNode->next = deleteNode->next;
free(deleteNode);
看着是不是比插入还简单,性能还要比顺序表要快很多
总结
1.顺序表逻辑地址和物理地址都是连续的,链表逻辑地址连续,地址不需要连续
2.利用顺序表的物理地址与索引的关系,如果设置成key-value的形式是不是查找更快
3.链表的延伸,双向链表,循环链表等