顺序表与链表的对比(二)

上一篇文章《顺序表与链表的对比(一)》中给小伙伴们分享了顺序表与链表的初始化及定义,那么这篇将详细的给小伙伴们分享关于它们增(插入)的一些知识。

顺序表与链表的增

增可以理解为往里面添加数据,一般也叫插入数据。插入的方式分几种,可以选择在头部插入(简称 ”头插“ ),也可以在尾部插入(简称 “尾插” ),中间插入也可以,适用于不同的应用场景。

顺序表插入应用场景

顺序表使用连续的内存空间来存储数据,这使得数据的访问非常快速,因为可以通过索引直接访问任何元素。

优点

  • 快速访问:通过索引可以直接访问任何元素,时间复杂度为O(1)。

  • 内存局部性:由于数据存储在连续的内存空间中,CPU缓存可以更有效地工作,提高访问速度。

缺点

  • 固定大小:数组的大小在创建时确定,扩容需要重新分配内存并复制数据,成本较高。

  • 插入和删除成本高:在数组中间插入或删除元素需要移动大量元素,时间复杂度为O(n)。

应用场景

  1. 数据访问频繁:当需要频繁访问数据时,数组是更好的选择,因为它提供了快速的随机访问能力。

  2. 数据量固定:当数据量不会频繁变化时,使用数组可以避免动态扩容的开销。

  3. 简单实现:对于简单的应用,数组的实现更直观和简单。

链表插入应用场景

链表通过节点之间的链接来存储数据,每个节点包含数据和指向下一个节点的指针。

优点

  • 动态大小:链表的大小可以动态变化,不需要预先分配固定大小的内存。

  • 插入和删除效率高:在已知位置的节点前后插入或删除节点只需要改变指针,时间复杂度为O(1)。

缺点

  • 访问速度慢:访问链表中的元素需要从头节点开始遍历,时间复杂度为O(n)。

  • 内存开销:每个节点需要额外的内存来存储指针。

应用场景

  1. 频繁插入和删除:当需要频繁在数据结构中插入或删除元素时,链表是更好的选择,因为它可以高效地处理这些操作。

  2. 数据量变化大:当数据量可能会有较大变化时,链表可以动态调整大小,避免频繁扩容的开销。

  3. 内存碎片:在内存碎片较多的环境中,链表可以更好地利用内存。

选择顺序表还是链表,主要取决于应用的具体需求:

  • 如果应用需要频繁访问元素,且数据量相对固定,顺序表是更好的选择。

  • 如果应用需要频繁插入和删除元素,且数据量可能会有较大变化,链表是更好的选择。

在实际应用中,还可以根据具体情况选择其他数据结构,如栈、队列、哈希表等,以满足特定的需求

顺序表进行头插或者中间插入时,需保证数组空间充足,因为将首位置腾出空来时,需要将以前的数组元素往后挪位置,时间复杂度位O(n),若数组空间不够,容易引起数组越界问题。如果需要进行频繁的头插时,可以考虑使用链表结构。不过顺序表的尾插还是方便的,时间复杂度位O(1)但也要注意数组空间是否充足。

链表进行头插时,效率比顺序表要高,因为它不需要挪动数据,只需改变指针指向即可。但是进行中间插入时,如果不知道插入位置,则需要遍历链表,时间复杂度位O(n),也挺麻烦。先看代码吧~

1.顺序表的头插

// 头插法插入元素
void headInsert(SL *list, int x)
 {
    if (list->n == list->capacity) {
        // 如果空间满了,扩容
        list->capacity *= 2;
        list->arr = (int*)realloc(list->arr, list->capacity * sizeof(int));
    }
    // 元素后移
    for (int i = list->n; i > 0; i--) {
        list->arr[i] = list->arr[i - 1];
    }
    // 插入新元素
    list->arr[0] = x;
    // 更新长度
    list->n++;
}

2.1 单链表的头插

// 头插法插入
void insertAtHead(Node** head, int data) 
{
    Node* newNode = createNode(data);
    newNode->next = *head; 
    //注意赋值先后顺序!!!
    *head = newNode;
}

2.2 双链表的头插

// 头插法插入
void insertAtHeadDoubly(Node** head, int data)
{
    Node* newNode = createNode(data);
    newNode->next = *head;
    if (*head != NULL)
    {
        (*head)->prev = newNode;
    }
    *head = newNode;
}

3.顺序表的尾插

顺序表的尾插其实跟头插差不多。只不过不需要挪动数据,注意好数组空间是否充足就行。

// 尾插法插入元素
void insertAtTail(SeqList *list, int data) 
{
    if (list->n == list->capacity)
    {
        // 如果空间满了,扩容
        list->capacity *= 2;
        list->arr = (int*)realloc(list->arr, list->capacity * sizeof(int));
    }
    list->arr[list->n] = data;
    list->n++;
}

4.1单链表的尾插

链表进行尾插时,需要检查链表是否为空,防止出现空指针问题。

// 尾插法插入
void insertAtTail(Node** head, int data) 
{
    Node* newNode = createNode(data);
    if (*head == NULL) 
    {
        *head = newNode;
        return;
    }
    Node* temp = *head;
    while (temp->next != NULL) 
     {
        temp = temp->next; //找尾结点
    }
    temp->next = newNode;
}

4.2 双链表的尾插

与单链表一样,需检查链表是否为空,不过如果带有哨兵位的头节点时,可以省略。

// 尾插法插入
void insertAtTailDoubly(Node** head, int data) 
{
    Node* newNode = createNode(data);
    if (*head == NULL)
    {
        *head = newNode;
        return;
    }
    Node* temp = *head;
    while (temp->next != NULL) 
    {
        temp = temp->next;
    }
    temp->next = newNode;
    newNode->prev = temp;
}

5.1单链表的随机插入

如果知道需要插入的位置时,就不用遍历,另外这里可以再单独写一个函数用来查找位置,通过其返回的指针,来进行插入(也可分前插和后插),后续删除也会使用到。

// 指定位置插入
void insertAtPosition(Node** head, int data, int position) 
{
    if (position == 0)
     {
        insertAtHead(head, data);
        return;
    }
    Node* newNode = createNode(data);
    Node* temp = *head;
    for (int i = 0; i < position - 1 && temp != NULL; i++)
     {
        temp = temp->next;
    }
    if (temp == NULL)
     {
        free(newNode);
        return; // 位置无效
    }
    newNode->next = temp->next;
    temp->next = newNode;
}

5.2双链表的随机插入

// 指定位置插入
void insertAtPositionDoubly(Node** head, int data, int position) 
{
    if (position == 0) 
    {
        insertAtHeadDoubly(head, data);
        return;
    }
    Node* newNode = createNode(data);
    Node* temp = *head;
    for (int i = 0; i < position - 1 && temp != NULL; i++) 
    {
        temp = temp->next;
    }
    if (temp == NULL) 
    {
        free(newNode);
        return; // 位置无效
    }
    newNode->next = temp->next;
    if (temp->next != NULL) 
    {
        temp->next->prev = newNode;
    }
    temp->next = newNode;
    newNode->prev = temp;
}

希望你开心吖@

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值