数据结构之单向链表

本文详细介绍了单向链表的基本概念,包括链表结构、元素的插入、删除、查找、索引获取和修改操作,以及C++代码实现,展示了如何在实际编程中使用单向链表的数据结构。
摘要由CSDN通过智能技术生成

单向链表

一、单向链表的概念

对于顺序存储的结构,最大的缺点就是:插入和删除的时侯需要移动大量的元素,所以基于前人的智慧,他们发明了链表。

链表是由一个个结点组成。每个结点之间通过链接关系串联起来,每个结点都有一个后继结点,最后一个结点的后继结点为空结点(NULL)。

由链接关系A->B组织起来的两个结点,B被称为A的后继结点,A被称为B的前驱节点。链表分为单向链表、双向链表、循环链表等等。本文只介绍单向链表。

一个链表结点由两部分组成:数据域和指针域。数据可以是任意类型,由编码的人自行指定。指针域指向后继结点的地址。

二、单向链表的元素插入

1、元素插入的概念

单向链表的元素插入,就是指给定一个索引 i 和一个元素data,生成一个值为data的结点,并且插入到第 i 个位置上。

2、元素插入的步骤

第1步、判断插入位置是否合法,如果不合法则抛出异常(比如:原本只有5个元素,给定的索引是100,那显然这个位置是不合法的)。

第2步、对给定的元素,生成一个链表结点。

第3步、如果插入位置是0,则直接把生成的结点的后继结点,设置为当前的链表的头结点,并且把生成的结点设置为新的链表头。

第4步、如果插入的位置不是0,则遍历到插入位置的前一个位置,把生成的结点插入进来。

第5步、更新链表的大小,即对链表的元素执行加一操作。

3、代码实现

void LinkedList::insert(int i, eleType value){
    if(i < 0 || i > size){
        throw std::out_of_range("Invalid position"); //需要引入头文件 #include<stdexcept>
    }
    ListNode* newnode = new ListNode(value); //生成一个新结点
    if(i == 0){
        newnode -> next = head;
        head = newnode;
    }else{
        ListNode* curr = head;
        for(int j = 0; j < i - 1; j++){
            curr = curr -> next;
        }
        newnode -> next = curr -> next;
        curr -> next = newnode;
    }
    size++;
}

三、单向链表的元素删除

1、元素删除的概念

单向链表的元素删除,就是给定一个索引 i,将从链表头开始数到的第 i 个结点删除。

2、元素删除的步骤

第1步、判断删除位置是否合法,如果不合法则抛出异常。

第2步、如果删除位置为首个结点,直接把链表头更新为它的后继结点。

第3步、如果删除位置非首个结点,则遍历到要删除位置的前一个结点,并且把前一个结点的后继结点设置为它后继的后继。

第4步、更新链表的大小,也就是把链表大小执行减一操作。

3、代码实现

void LinkedList::remove(int i){
    if(i < 0 || i > size){
        throw std::out_of_range("Invalid position");
    }
    if(i == 0){
        ListNode* tmp = head;
        head = head -> next;
        delete tmp;
    }else{
        ListNode* curr = head;
        for(int j = 0; j < i - 1; j++){
            curr = curr -> next;
        }
        ListNode* tmp = curr -> next;
        curr -> next = tmp -> next;
        delete tmp;
    }
    size--;
}

四、单向链表的元素查找

1、元素查找的概念

单向链表的元素查找,是指在链表中查找指定元素x是否存在,如果存在则返回该节点,否则返回NULL。由于需要遍历整个链表进行元素对比,所以查找的时间复杂度为O(n)。

2、元素查找的步骤

第1步、遍历整个链表,对链表中的每个元素,和指定的元素比较,如果相等则返回当前遍历到的结点;

第2步、如果遍历完整个链表,都没有找到相等的元素,则返回NULL。

3、代码实现

ListNode* LinkedList::find(eleType value){
    ListNode* curr = head;
    while(curr && curr -> data != value){
        curr = curr -> next;
    }
    return curr;
}

五、单向链表的元素索引

1、元素索引的概念

单向链表的元素索引,是指给定一个索引值 i,从链表头节点开始数,数到第 i 个结点并且返回它,时间复杂度O(n)。

2、元素索引的步骤

第1步、首先判断给定的索引是否合法,如果不合法则抛出异常;

第2步、直接通过索引访问即可获得对应的元素。

3、代码实现

ListNode* LinkedList::get(int i){
    if(i < 0 || i > size){
        throw std::out_of_range("Invalid position");
    }
    ListNode* curr = head;
    for(int j = 0; j < i; j++){
        curr = curr -> next;
    }
    return curr;
}

六、单向链表的元素修改

1、元素修改的概念

单向链表的元素修改是指将链表中指定索引的元素更新为新的值。

2、元素修改的步骤

直接通过索引访问即可获得对应的结点,修改成指定的值;

3、代码实现

ListNode* LinkedList::get(int i){
    if(i < 0 || i > size){
        throw std::out_of_range("Invalid position");
    }
    ListNode* curr = head;
    for(int j = 0; j < i; j++){
        curr = curr -> next;
    }
    return curr;
}
​
void LinkedList::update(int i, eleType value){
    get(i) -> data = value;
}

七、手搓单向链表代码实现

#include<iostream>
#include<stdexcept>
​
using namespace std;
​
#define eleType int
​
struct ListNode{
    eleType data;   //数据域 
    ListNode* next; //指针域 
    
    ListNode(eleType x) : data(x), next(NULL) {}    //初始化 
};
​
class LinkedList{
private:
    ListNode* head;
    int size;
    
public:
    LinkedList() : head(NULL), size(0) {}   //初始化 
    ~LinkedList();  //析构函数 
    void insert(int i, eleType value);  //增 
    void remove(int i);                 //删 
    ListNode* find(eleType value);      //查 
    ListNode* get(int i);               //查 
    void update(int i, eleType value);  //改 
    void print();                       //调试函数 
    
};
​
LinkedList::~LinkedList(){
    ListNode* curr = head;
    while(curr){    //curr不为空(curr != NULL) 
        ListNode* tmp = curr;
        curr = curr -> next;
        delete tmp;
    }
} 
​
void LinkedList::insert(int i, eleType value){
    if(i < 0 || i > size){
        throw std::out_of_range("Invalid position"); //需要引入头文件 #include<stdexcept>
    }
    ListNode* newnode = new ListNode(value); //生成一个新结点
    if(i == 0){
        newnode -> next = head;
        head = newnode;
    }else{
        ListNode* curr = head;
        for(int j = 0; j < i - 1; j++){
            curr = curr -> next;
        }
        newnode -> next = curr -> next;
        curr -> next = newnode;
    }
    size++;
}
​
void LinkedList::remove(int i){
    if(i < 0 || i > size){
        throw std::out_of_range("Invalid position");
    }
    if(i == 0){
        ListNode* tmp = head;
        head = head -> next;
        delete tmp;
    }else{
        ListNode* curr = head;
        for(int j = 0; j < i - 1; j++){
            curr = curr -> next;
        }
        ListNode* tmp = curr -> next;
        curr -> next = tmp -> next;
        delete tmp;
    }
    size--;
}
​
ListNode* LinkedList::find(eleType value){
    ListNode* curr = head;
    while(curr && curr -> data != value){
        curr = curr -> next;
    }
    return curr;
}
​
ListNode* LinkedList::get(int i){
    if(i < 0 || i > size){
        throw std::out_of_range("Invalid position");
    }
    ListNode* curr = head;
    for(int j = 0; j < i; j++){
        curr = curr -> next;
    }
    return curr;
}
​
void LinkedList::update(int i, eleType value){
    get(i) -> data = value;
}
​
void LinkedList::print(){
    ListNode* curr = head;
    while(curr){
        cout << curr -> data << ' ';
        curr = curr -> next;
    }
    cout << endl;
}
​
​
int main(){
    LinkedList list;
    list.insert(0, 10);
    list.insert(1, 20);
    list.insert(2, 30);
    list.insert(3, 40);
    list.insert(4, 50);
    list.print();//10 20 30 40 50
    list.remove(1);
    list.print();//10 30 40 50
    list.update(2,60);
    list.print();//10 30 60 50
    ListNode* tmp = list.find(30);
    cout << tmp -> data << endl;//30
    cout << list.get(3) -> data << endl;//50
​
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值