数据结构 | 单链表的增删改查(带头节点)

本文详细介绍了一种带头节点的单链表数据结构的实现方法,包括头插、尾插、头删、尾删等基本操作,以及如何通过Entry类封装节点属性,通过Link类封装链表操作。

带头节点 的单链表:在初始化时产生一个headEntry结点,其value域和next域均为空。其链表结构如下:
在这里插入图片描述
注意:带头节点和不带头节点的单链表区别就在headEntry

带头节点的单链表我们需要从headEntry的下个节点开始进行各种操作,而不带头节点的从headEntry开始操作,操作方法大同小异,适当分析,搞清楚起始点即可

  • Entry类封装节点属性,包括value域及next域
  • 代码实现如下:
public class Entry<E extends Comparable<E>> {
    private E value;
    private Entry next;
    public Entry(){
    }
    public Entry(E value){
        this.value=value;
    }
    public void setValue(E value) {
        this.value = value;
    }
    public void setNext(Entry next) {
        this.next = next;
    }
    public E getValue() {
        return value;
    }
    public Entry getNext() {
        return next;
    }
}
  • Link类封装头插尾插头删尾删等操作
  • 代码实现如下:
package linklist;

//带头节点的单链表
public class Link<T extends Comparable<T>> {
    private Entry<T> headEntry;
    private Entry<T> tailEntry;
    public Link(){
        headEntry=new Entry<>();
    }
    //头插
    public void addHead(T value){
        Entry<T> newEntry=new Entry<>(value);
        //头节点后无元素
        if(headEntry.getNext()==null){
            headEntry.setNext(newEntry);
            tailEntry=newEntry;
        }
        newEntry.setNext(headEntry.getNext());
        headEntry.setNext(newEntry);//更新头节点
        if(headEntry.getNext().getNext()==null){
            //头节点后只有一个元素时,需要更新尾节点
            //头节点后存在多个元素时,不需要更新尾节点
            tailEntry=newEntry;
        }
    }
    //尾插
    public void addTail(T value){
        Entry<T> newEntry=new Entry<>(value);
        //头节点后无元素
        if(headEntry.getNext()==null){
            headEntry.setNext(newEntry);
            tailEntry=newEntry;
        }
        tailEntry.setNext(newEntry);
        tailEntry=newEntry;//更新尾节点
    }
    //头删
    public void deleteHead(){
        if(headEntry.getNext()==null){//防止空指针异常
            return;
        }
        Entry p=headEntry.getNext();
        headEntry.setNext(headEntry.getNext().getNext());
        p.setValue(null);//防止内存泄漏
        p.setNext(null);
    }
    //尾删
    public void deleteTail(){
        if(headEntry.getNext()==null){
            return;
        }
        Entry<T> p=headEntry.getNext();
        for(;p.getNext().getNext()!=null;p=p.getNext()){
            ;   //找尾部前驱p
        }
        p.getNext().setValue(null);//防止内存泄漏
        p.setNext(null);
        tailEntry=p;
    }
    public void deleteValue(T value){
        //头节点后无元素
        if(headEntry.getNext()==null){
            return;
        }
        //若头节点后的第一个元素即为需要删除的元素,则相当于头删
        if(headEntry.getNext().getValue().compareTo(value)==0){
            deleteHead();
            if(headEntry==tailEntry) {
                tailEntry.setNext(null);//更新尾节点
            }
            return;
        }
        //若尾节点元素即为需要删除的元素,则相当于尾删
        if(tailEntry.getValue().compareTo(value)==0){
            deleteTail();
            if(headEntry==tailEntry) {
                tailEntry.setNext(null);
            }
            return;
        }
        //遍历链表,寻找指定删除元素
        for(Entry<T> p = headEntry.getNext(); p.getNext().getNext()!=null; p=p.getNext()){
            if(p.getNext().getValue().compareTo(value)==0){
                p.getNext().setValue(null);
                p.setNext(p.getNext().getNext());
                break;
            }
        }
    }
### 单链表增删改查操作 单链表是一种常见的数据结构,用于实现动态集合。以下是单链表增删改查操作的具体方法代码示例。 #### 1. 节点结构定义 在C语言中,单链表的基本节点结构通常包含两个部分:数据域指针域。以下是一个典型的节点定义[^2]: ```c typedef struct Node { int data; // 数据域 struct Node* next; // 指针域,指向下一个节点 } Node; ``` #### 2. 初始化单链表 初始化单链表时,可以选择是否使用头节点带头节点的初始化方式更加通用,适合多种操作场景[^2]: ```c Node* init_list() { Node* head = (Node*)malloc(sizeof(Node)); if (head == NULL) { printf("内存分配失败\n"); return NULL; } head->next = NULL; // 初始化为空链表 return head; } ``` #### 3. 插入操作 插入操作分为头插法尾插法两种常见方式。以下分别是头插法尾插法的实现[^2]: **头插法**(新节点插入到链表头部): ```c void insert_head(Node* head, int value) { Node* new_node = (Node*)malloc(sizeof(Node)); if (new_node == NULL) { printf("内存分配失败\n"); return; } new_node->data = value; new_node->next = head->next; // 新节点指向原首节点 head->next = new_node; // 头节点指向新节点 } ``` **尾插法**(新节点插入到链表尾部): ```c void insert_tail(Node* head, int value) { Node* new_node = (Node*)malloc(sizeof(Node)); if (new_node == NULL) { printf("内存分配失败\n"); return; } new_node->data = value; new_node->next = NULL; Node* p = head; while (p->next != NULL) { // 找到最后一个节点 p = p->next; } p->next = new_node; // 将新节点链接到最后 } ``` #### 4. 删除操作 删除操作需要遍历链表找到目标节点,并调整指针以移除该节点。以下是删除指定值节点的实现: ```c void delete_node(Node* head, int value) { Node* p = head; while (p->next != NULL) { if (p->next->data == value) { Node* temp = p->next; // 保存待删除节点 p->next = p->next->next; // 调整指针 free(temp); // 释放内存 return; } p = p->next; } printf("未找到值为 %d 的节点\n", value); } ``` #### 5. 修改操作 修改操作是指定位到目标节点后更新其数据域。以下是修改指定值节点的实现[^2]: ```c void modify_node(Node* head, int old_value, int new_value) { Node* p = head->next; while (p != NULL) { if (p->data == old_value) { p->data = new_value; return; } p = p->next; } printf("未找到值为 %d 的节点\n", old_value); } ``` #### 6. 查询操作 查询操作用于查找链表中是否存在指定值的节点。以下是查询指定值节点的实现[^3]: ```c int search_node(Node* head, int value) { Node* p = head->next; while (p != NULL) { if (p->data == value) { return 1; // 找到返回1 } p = p->next; } return 0; // 未找到返回0 } ``` #### 7. 遍历操作 遍历操作用于逐个访问链表中的所有节点。以下是遍历链表的实现[^3]: ```c void traverse_list(Node* head) { Node* p = head->next; if (p == NULL) { printf("链表为空\n"); return; } printf("链表内容如下:\n"); while (p != NULL) { printf("%d ", p->data); p = p->next; } printf("\n"); } ``` ### 性能分析 - **时间复杂度**:插入、删除、修改查询操作的时间复杂度均为O(n),其中n为链表长度。 - **空间复杂度**:单链表的空间复杂度为O(n),因为每个节点都需要额外的指针存储空间[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值