C++ 1.STL-vector 2.STL-list 3.数组模拟单向链表 详解配例题 通俗易懂

一.STL-vector

1.含义:

本质上是一个变长数组,可以根据其中装的元素的数量,动态分配内存空间

2.头文件:

#include<vector>

3.定义一个type类型的变长数组

vector<type>数组名;

4.常用操作

①往末尾追加元素e
   a.pish_back(e);//无返回值,第一个元素在a[0]
②引用随机位置idx元素
  a[idx];
③获取元素数量
  a.size();
定义a并分配空间
  vector<int> a(n);//定义a,同时分配n个大小,即a[0]~a[n-1]
  a[n-1]=n-1;

5.定义结构体

struct node{int x,y;};
  vector<node> a;
------>定义了一个结构体数组a

a.push_back((node){1,2});//添加了一个元素
  a[0].x;//结果为1
  a[0].y;//结果为2

6.定义二维数组

vector<vector<int>> a;

二.链表的基本概念

链表是一种线性表。

线性表中,除了第一个元素没有前驱,最后一个元素没有后继以外,其他每一个元素都有唯一的前驱和后继。

1.链表和数组的区别

链表和数组都属于线性表,区别是:
数组在计算机中是连续 分配内存的;
链表在计算机中是不连续分配内存的。

2.链表的实现方式

单向链表中的每一个元素包含3部分信息:
(1)地址:在内存中的位置
(2)数据域:存放数据
(3)地址域:存放后继的地址
(地址相当于变量在计算机内存里的"门牌号码")

3.头指针

链表中第一个元素没有前驱,我们使用一个头指针指向它的位置,头指针是一个专门存储地址的变量。

4.空地址

链表中最后一个元素没有后继,我们在它的地址域放一个空地址,NULL。

三、链表的特点

链表由于在内存中不是连续存储的,所以:

  1. 不支持随机访问第i个元素。每次查询某个元素需要遍历整个链表,时间复杂度为O(n)。
  2. 插入和删除元素不需要移动后面的元素,只需要修改一些元素的地址域,时间复杂度为0(1)。但是查找到插入和删除的位置需要花时间。
1.插入操作

链表中,已经元素p,在后面插入一个元素t,要先确定p后面的元素地址,可以表示为p->next,接下来进行两步操作

  1. 把t指向p->next,
  2. 把p指向t。
    代码表示为:
    t->next=p->next;
    p->next=t;

【注意】:两步的顺序不能颠倒!

2.删除操作

链表中,假设我们要在a和b之间的结点删除,需要将a的地址域赋值为p的地址域。

通常还跟着一步删除结点p,释放结点p的内存空间。

3.双向链表

有的链表中,每个元素不仅有存储后继的地址域,还有存储其前驱的地址域。这样的链表叫双向链表。

双向链表中的节点p,它有4条线,llink表示前驱,rlink表示后继,那么可以用代码表示为:
(1)p->llink
(2)p->rlink
(3)p->llink->rlink
(4)p->rlink->llink

4.循环链表

有的链表中,最后一个元素的后继不是NULL,而是链表的第一个元素。这样的链表叫循环链表。

四、代码实现

1.头文件与定义链表
  • 头文件:#include<list>
  • 定义:list<数据类型> 链表名;
  • 例子:
    • list<int> L1;//定义一个int类型的链表L1
    • list<double> L2;//定义一个double类型的链表L2
2.求链表长度与遍历链表

定义一个链表l:list<int> l;

1.求链表长度:l.size();
2.遍历链表:通过地址遍历
  • 方法1:for循环
cpp 复制代码
list<int> ::iterator it;//定义一个迭代器it,it表示地址
for(it=l.begin();it!=l.end();it++){
    cout<<*it<<" ";//输出这个地址中的数据
}

或者
for(auto it=l.begin();it!=l.end();it++){
   cout<<*it<<" ";
}
  • 方法2:while循环
cpp 复制代码
list<int> l;
    list<int> ::iterator it=l.begin();
    //当迭代器没有达到链表尾部,迭代器往后移动
    while(it!=l.end()){
        out<<*it<<" ";//输出这个地址中的数据
        it++;
    }
3.删除值为e的元素
cpp 复制代码
①找到值为e的元素
  list<int>l;
  list<int>::iterator it=l.begin();
  while(it!=l.end()&&*it!=e){
      it++;
  }
②删除元素
  l.erase(it);//函数会返回指向被删除元素后一个元素的迭代器
4、插入元素
cpp 复制代码
①向尾部插入新元素e:l.push_back(e);
②向元素x前插入一个e
  先找到x的位置
    list<int>l;
    list<int>::iterator it=l.begin();
    while(it!=l.end()&&*it!=x){
        it++;
    }
  在插入
    l.insert(it,e);//函数返回一个指向新元素e的迭代器
5、简记

线性表 前驱和后继唯一

第一个元素无前驱 最后一个元素无后继 数组地址连续

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
int a[10];
int main() {
    for(int i=0; i<=9; i++) {
        cout << &a[i] << ' ';//取a[i]存储的地址
    }
    return 0;
}
重要性质

链表不连续分配内存 1. 需要掌握第一个元素的地址(头指针) 2. 每个元素除了本身的元素值(数据域),还需要下一个元素的地址(地址域) 3. 最后一个元素的下一个地址为空(NULL)

特点
  1. 访问元素要遍历整个链表,时间复杂度O(n)

  2. 插入和删除元素都是O(1) 其他链表 双向链表:地址域有两个,存储前驱和后继的地址域 循环链表:最后一个元素的地址域不是NULL,而是第一个元素的地址

STL-list
cpp 复制代码
#include <list>
list<int> a;//定义链表

#include <bits/stdc++.h>
using namespace std;
list<int> a;
int n, t;
int main() {
    cin >> n;
    while(n--) {
        cin >> t;
        a.push_back(t);
    }
    cout << a.size() << endl;
    for(auto it=a.begin(); it!=a.end(); it++) {
        cout << *it << ' ';
    }
    return 0;
}

插入函数 a.insert(it, e);//在it前插入e 该函数会返回指向新元素e的迭代器 it = a.insert(it, e);

删除函数 a.erase(it);//删除it指向的元素 该函数会返回指向删除元素后一个元素的迭代器 it = a.erase(it);

五、课堂题代码

5035 多次删除链表里的元素
cpp 复制代码
#include<iostream>
#include<list>
using namespace std;

int n,t;
list<int> l;
list<int>::iterator it;
int main(){
    cin>>n;
    while(n--){//输入n个数存入链表 
        cin>>t;
        l.push_back(t);
    }
    //输入若干数,删除这些数
    while(cin>>t){
        //删除链表中值为t的元素
        //借助迭代器it遍历链表,得到数据域为t时,进行删除
        for(it=l.begin();it!=l.end();it++){
            if(*it == t){
                l.erase(it);
                break;
            }
        } 
    } 
    //输出剩下的元素
    for(it=l.begin();it!=l.end();it++){
        cout<<*it<<' ';
    }  
    return 0;
}
5038 删除指定的倒数第k个元素
cpp 复制代码
#include<iostream>
#include<list>
using namespace std;
/*
倒数第k个数
正数:第n-k+1个数

遍历链表的时候计数,计数达到k,删除 

指定元素不存在则不删除:k<1或者k>n 
*/
int n,k,t;
int cnt;
list<int> l;
list<int>::iterator it;
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>t;
        cnt++;
        if(cnt==n-k+1) continue;
        l.push_back(t);
    }
    //遍历链表
    for(it=l.begin();it!=l.end();it++){
        cout<<*it<<' ';
    } 
    return 0;
}

六、数组模拟链表

cpp 复制代码
数组模拟链表 以空间换时间
方法一:结构体
    struct NODE {
        int val;//数值域
        int r;//地址域
    } a[105];
    int main() {
        int h = 1;//头指针
        a[1] = {10, 3};
        a[3] = {15, 6};
        a[6] = {12, -1};
        for(int it=h; it!=-1; it=a[it].r) {
            cout << a[it].val << ' ';
        }
        return 0;
    }
方法二:两个数组(推荐)
    int val[105];//数值域
    int r[105];//地址域
    //int l[105];如果是双向链表还需前驱的地址域 也就是left
    int main() {
        int h = 1;//头指针

        val[1] = 10;
        val[3] = 15;
        val[6] = 12;

        r[1] = 3;
        r[3] = 6;
        r[6] = -1;

        for(int it=h; it!=-1; it=r[it]) {
            cout << val[it] << ' ';
        }
        return 0;
    }
插入和删除元素
    int h = 1;//头指针

    val[1] = 10;
    val[3] = 15;
    val[6] = 12;

    r[1] = 3;
    r[3] = 6;
    r[6] = -1;

    //在15和12之间插入13
    val[4] = 13;
    r[4] = r[3];
    r[3] = 4;
    //删除13
    r[3] = r[r[3]];
相关推荐
理论最高的吻4 小时前
108. 将有序数组转换为二叉搜索树【 力扣(LeetCode) 】
c++·算法·leetcode·职场和发展·二分查找·平衡二叉树
感哥1 天前
C++ 三之法则、五之法则和零之法则
c++
感哥2 天前
C++ 迭代器
c++
tkevinjd3 天前
C++线程池学习 Day07
c++
TangHao19873 天前
第一章 基础(Chapter 1 fundentals)
c++
沐怡旸3 天前
【底层机制】std::move 解决的痛点?是什么?如何实现?如何正确用?
c++·面试
tongsound4 天前
ros2 humble slam仿真环境搭建(turtlebot3 & Gazebo)
c++·docker
沐怡旸5 天前
【底层机制】std::weak_ptr解决的痛点?是什么?如何实现?如何正确用?
c++·面试
River4165 天前
Javer 学 c++(十六):对象特性篇(上)
c++·后端