2.4 线性表的插入删除

1. 链表的插入删除

1. 单链表插入删除

图1. 单链表插入结点

        策略: p指向要插入的位置的前一个结点, s指向要插入的结点. 首先要设置s结点的next指针指向p的下一个结点, 即s->next = p->next; 然后再设置p的next指针指向s, 即p->next = s; 重点: p的后继结点的地址信息不要丢掉.

 图2. 单链表删除结点

        策略: p指向要删除的结点的前面一个结点, s指向要删除的结点. 直接更新p的next指针指向s的后继结点, 即p->next = s->next; 然后释放s指向的结点的空间, 即free(s); 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode;


/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode* p) {
    if (p == NULL) {
        return false;
    }
    return true;
}

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode* p) {
    while (p != NULL) {
        // 对当前节点进行操作,打印节点的数据
        printf("%d\n", p->data);

        // 移动到下一个节点
        p = p->next;
    }
}

/// <summary>
/// 插入元素
/// </summary>
/// <param name="p">插入位置前面的元素</param>
/// <param name="q">要插入的元素</param>
void insertElement(LNode* p, LNode* q) {
    q->next = p->next;
    p->next = q;
}

/// <summary>
/// 删除元素
/// </summary>
/// <param name="p">要删除的元素的前一个元素</param>
/// <returns></returns>
LNode* deleteElement(LNode* p) {
    LNode* q = p->next;
    p->next = q->next;
    q->next = NULL;
    return q;
}

int main()
{
    LNode* A;
    A = new LNode;
    LNode* B;
    B = new LNode;
    LNode* C;
    C = new LNode;

    A->data = 10;
    B->data = 20;
    C->data = 30;

    A->next = B;
    B->next = NULL;
    C->next = NULL;

    LNode* p = A;
    LNode* q = C;
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        insertElement(p, q);
        traverseLinkedList(p);
        printf("---------------------------\n");
        LNode* q = deleteElement(p);
        free(q);
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}

代码1: 单链表的插入删除(没有考虑特殊情况)

        特殊情况: 在链表的第一个结点之前插入结点或者删除第一个结点. 

图3. 在含头结点的单链表的第一个结点之前插入结点

        策略: head指向要插入位置的前面一个结点, s指向要插入的结点. 先设置s的next指针指向head的后继结点, 即s->next = head->next; 再设置head的next指针指向s, 即head->next = s; 这与普通情况下的插入操作代码是统一的, 因此比较好. 

 图4. 在不含头结点的单链表的第一个结点之前插入结点

        策略: head指向第一个结点, s指向要插入的结点. 现在要将s结点插入到head结点的前面, 先将s的next指针指向head, 即s->next = head; 然后更新head指向新的首个结点s, 即head = s;

图5. 含头结点的单链表删除第一个结点

        策略: head指向要删除的结点的前面一个结点, s指向要删除的结点. 先设置head的next指针指向s的后继结点, 即head->next = s->next; 再释放s所指向结点的空间, 即free(s); 这与普通情况下的删除操作代码是统一的, 因此比较好. 

 图6. 不含头结点的单链表删除第一个结点

        策略: head指向第一个结点, p指向要删除的结点, 先设置head结点指向新的首个结点, 即head = head->next; 再释放p所指结点的空间, 即free(p); 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode;


/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode* p) {
    if (p == NULL) {
        return false;
    }
    return true;
}

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode* p) {
    while (p != NULL) {
        // 对当前节点进行操作,打印节点的数据
        printf("%d\n", p->data);

        // 移动到下一个节点
        p = p->next;
    }
}

/// <summary>
/// 插入元素
/// </summary>
/// <param name="p">插入位置前面的元素</param>
/// <param name="q">要插入的元素</param>
void insertElement(LNode* p, LNode* q) {
    q->next = p->next;
    p->next = q;
}

/// <summary>
/// 第一个结点之前插入元素
/// </summary>
/// <param name="p">第一个结点</param>
/// <param name="q">要插入的元素</param>
void insertFirstElement(LNode*& p, LNode* q) {   //在C++中,指针类型属于值类型。这里需要改变p的值, 因此传递p的引用
    q->next = p;
    p = q;
    printf("第一个数据: %d\n", p->data);
}

/// <summary>
/// 删除元素
/// </summary>
/// <param name="p">要删除的元素的前一个元素</param>
/// <returns></returns>
LNode* deleteElement(LNode* p) {
    LNode* q = p->next;
    p->next = q->next;
    q->next = NULL;
    return q;
}

/// <summary>
/// 删除第一个结点
/// </summary>
/// <param name="p">第一个结点</param>
/// <returns></returns>
LNode* deleteFirstElement(LNode*& p) {  //在C++中,指针类型属于值类型。这里需要改变p的值, 因此传递p的引用
    LNode* q = p;
    p = p->next;
    printf("删除的第一个数据: %d\n", q->data);
    return q;
}

int main()
{
    LNode* A;
    A = new LNode;
    LNode* B;
    B = new LNode;
    LNode* C;
    C = new LNode;

    A->data = 10;
    B->data = 20;
    C->data = 30;

    A->next = B;
    B->next = NULL;
    C->next = NULL;

    LNode* p = A;
    LNode* q = C;
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        insertFirstElement(p, q);
        traverseLinkedList(p);
        printf("---------------------------\n");
        LNode* q = deleteFirstElement(p);
        free(q);
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}

代码2: 在不含头结点的单链表的第一个结点之前插入结点或者删除第一个结点. 

        需要注意的是, 在C++中,指针类型属于值类型。在第一个结点之前插入结点或者删除第一个结点的函数中需要改变p的值, 因此传递p的引用. 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
}LNode;


/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode* p) {
    if (p == NULL) {
        return false;
    }
    return true;
}

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode* p) {
    while (p != NULL) {
        // 对当前节点进行操作,打印节点的数据
        if (p->data != 0) {
            printf("%d\n", p->data);
        }

        // 移动到下一个节点
        p = p->next;
    }
}

/// <summary>
/// 插入元素
/// </summary>
/// <param name="p">插入位置前面的元素</param>
/// <param name="q">要插入的元素</param>
void insertElement(LNode* p, LNode* q) {
    q->next = p->next;
    p->next = q;
}

/// <summary>
/// 删除元素
/// </summary>
/// <param name="p">要删除的元素的前一个元素</param>
/// <returns></returns>
LNode* deleteElement(LNode* p) {
    LNode* q = p->next;
    p->next = q->next;
    q->next = NULL;
    return q;
}

int main()
{
    LNode* H;
    H = new LNode;
    LNode* A;
    A = new LNode;
    LNode* B;
    B = new LNode;
    LNode* C;
    C = new LNode;

    H->data = NULL;
    A->data = 10;
    B->data = 20;
    C->data = 30;

    H->next = A;
    A->next = B;
    B->next = NULL;
    C->next = NULL;

    LNode* p = H;   //p指向头结点
    LNode* q = C;
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        //第一个结点之前即头结点之后插入元素
        insertElement(p, q);
        traverseLinkedList(p);
        printf("---------------------------\n");
        //删除第一个结点, 即删除头结点之后的元素
        LNode* q = deleteElement(p);
        free(q);
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}

代码3: 在含头结点的单链表的第一个结点之前插入结点或者删除第一个结点. 

        可以看出: 给链表设置头结点, 可以使得在第一个数据结点之前插入一个新结点和删除第一个数据结点的操作同表中部结点的这些操作统一起来, 方便写代码; 带头结点的链表, 其头指针值不随操作而改变, 可以减少错误

2. 双链表插入删除

 图7. 双链表结点插入

        策略: p指向要插入位置的前面一个结点, s指向要插入的结点. 先设置s的前驱与后继. 即s->next = p->next; s->prior = p; 再设置s的前驱的后继, 以及设置s的后继的前驱. 即 s->prior-next = s(这里s->prior与p是等效的); s->next-prior = s;

图8. 双链表结点删除

        策略: p指向要删除的结点的前面一个结点, s指向要删除的结点. 先设置s的前驱的后继为s的后继, 再设置s的后继的前驱为s的前驱, 即s->prior-next = s->next; s->next->prior = s->prior; 最后释放s所指的结点的空间. 即free(s); 

#include <iostream>

typedef struct LNode {
    int data;
    struct LNode* next;
    struct LNode* prior;
}LNode;

/// <summary>
/// 遍历输出链表各个结点的值
/// </summary>
/// <param name="firstNode"></param>
void traverseLinkedList(LNode* p) {
    p = p->next;
    while (p != NULL) {
        // 对当前节点进行操作,打印节点的数据
        printf("%d\n", p->data);

        // 移动到下一个节点
        p = p->next;
    }
}

/// <summary>
/// 判断链表是否非空
/// </summary>
/// <param name="p"></param>
bool linkedListIsNotNull(LNode* p) {
    if (p->next == NULL) {
        return false;
    }
    return true;
}

/// <summary>
/// 插入元素
/// </summary>
/// <param name="p">插入位置前面的元素</param>
/// <param name="s">要插入的元素</param>
void insertElement(LNode* p, LNode* s) {
    s->next = p->next;
    s->prior = p;
    p->next = s;
    s->next->prior = s;
}

/// <summary>
/// 删除元素
/// </summary>
/// <param name="s">要删除的元素</param>
/// <returns></returns>
LNode* deleteElement(LNode* s) {
    s->prior->next = s->next;
    s->next->prior = s->prior;

    s->next = NULL;
    s->prior = NULL;
    return s;
}


int main()
{
    LNode* H;
    H = new LNode;
    LNode* A;
    A = new LNode;
    LNode* B;
    B = new LNode;
    LNode* C;
    C = new LNode;

    H->data = NULL;
    A->data = 10;
    B->data = 20;
    C->data = 30;

    H->next = A;
    A->next = B;
    B->next = NULL;
    C->next = NULL;

    H->prior = NULL;
    A->prior = H;
    B->prior = A;
    C->prior = NULL;

    LNode* p = H;        
    LNode* s = C;
    bool isNotNull = linkedListIsNotNull(p);
    if (isNotNull) {
        insertElement(p, s);
        traverseLinkedList(p);
        printf("---------------------------\n");
        LNode* q = deleteElement(s);
        free(q);
        traverseLinkedList(p);
    }
    else {
        printf("链表为空!");
    }
}

代码4: 在含头结点的双链表的第一个结点之前插入结点或者删除第一个结点. 

        可以看出, 在双链表里要删除一个结点, 我们只需要知道这个结点的地址信息即可, 因为其相邻的结点的地址信息已在该结点的内部储存

2. 顺序表的插入删除

1. 顺序表的插入操作

 图9. 顺序表插入元素

        可插入下标位置p的取值范围是: 0到length; 

        当表长length等于数组长度maxSize的时候, 不可以再插入元素; 

        移动元素从后往前后移; 

        接下来写一个可以在顺序表中任一位置合法地插入一个新元素的函数. 所谓合法, ①插入元素的位置必须合法, 即0 <= p <= length; ②插入元素的时机必须合法, 即length < maxSize. 

#include <iostream>

/// <summary>
/// 数组最大长度
/// </summary>
const int MAX_SIZE = 10;

/// <summary>
/// 顺序表插入元素
/// </summary>
/// <param name="arr">要操作的数组</param>
/// <param name="length">插入元素前的数组长度</param>
/// <param name="p">插入位置</param>
/// <param name="e">插入元素</param>
/// <returns></returns>
int insertElement(int* arr, int& length, int p, int e) {
    if (p < 0 || p > length || length > MAX_SIZE) {
        return 0;       //不合法
    }
    for (int i = length - 1; i >= p; i--)       //注意这里是i--
    {
        arr[i + 1] = arr[i];
    }
    arr[p] = e;
    length++;
    return 1;
}

int main()
{
    int arr[MAX_SIZE] = { 1,2,3,4,5,6 };
    int length = 6;

    int result = insertElement(arr, length, 0, 9999);

    if (result == 1) {
        for (int i = 0; i < length; i++)
        {
            printf("%d\n", arr[i]);
        }
    }
}

代码5: 顺序表插入元素

2. 顺序表的删除操作

图10. 顺序表删除元素

        注意, 若强行修改length的值, 如将length修改为3, 其它的不变, 则此时顺序表所存的线性表中的元素只有0, 1, 2中的元素, 其它的元素虽然也在数组中, 但是它们已经不属于该顺序表中的元素了. 0 ~ length - 1才是顺序表中的元素所在范围. 除此之外的都不算顺序表中的元素

        所以如果想将顺序表清零, 只需length = 0即可. 其它的不用管, 虽然那些元素还留在数组中, 但对于该顺序表来说, 它是不可见的

        可删除元素下标p的取值范围: 0 ~ length - 1; 

        当表长length等于0时, 不可以再删除元素; 

        移动元素从前往后前移; 

#include <iostream>

/// <summary>
/// 数组最大长度
/// </summary>
const int MAX_SIZE = 10;

/// <summary>
/// 顺序表插入元素
/// </summary>
/// <param name="arr">要操作的数组</param>
/// <param name="length">插入元素前的数组长度</param>
/// <param name="p">插入位置</param>
/// <param name="e">插入元素</param>
/// <returns></returns>
int insertElement(int* arr, int& length, int p, int e) {
    if (p < 0 || p > length || length > MAX_SIZE) {
        return 0;       //不合法
    }
    for (int i = length - 1; i >= p; i--)       //注意这里是i--
    {
        arr[i + 1] = arr[i];
    }
    arr[p] = e;
    length++;
    return 1;
}

/// <summary>
/// 顺序表删除元素
/// </summary>
/// <param name="arr">要操作的数组</param>
/// <param name="length">数组删除元素前的长度</param>
/// <param name="p">要删除的位置</param>
/// <param name="e">要删除的元素</param>
/// <returns></returns>
int deleteElement(int* arr, int& length, int p, int& e) {
    if (p < 0 || p > length - 1 || length <= 0) {
        return 0;
    }
    e = arr[p];
    for (int i = p; i < length - 1; i++)
    {
        arr[i] = arr[i + 1];
    }
    length--;
    return 1;
}

int main()
{
    int arr[MAX_SIZE] = { 1,2,3,4,5,6 };
    int length = 6;
    int e;
    int result = deleteElement(arr, length, 0, e);

    if (result == 1) {
        printf("删除的元素: %d\n", e);
        for (int i = 0; i < length; i++)
        {
            printf("%d\n", arr[i]);
        }
    }
}

代码6: 顺序表删除元素

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值