C++数据结构——array、vector、链表

一、 array模板类

     C++中的数组类型是继承了c语言的特性,在使用数组的时候要注意数组越界操作问题。为了更安全的对数组进行操作,C++提出了数组模板类array。array内存空间为连续的一段地址,适用于提前已知所要存储的数据类型和数量、进行大量的查、改操作,不适用于含有大量交换、删除、增加数据的操作,该容器无法动态改变大小,所以说提前已知存储数据类型和数量

1、array模板类的定义

array模板类的声明

template  class array;

      array模板类中T为包含元素的类型(std::array::value_type),N为元素个数。数组类是固定大小的序列容器,array 定义的时候必须定义数组的元素个数。除了需要指定元素的类型和个数之外,和常规数组没有太大的差别。显然不能增加或删除元素

       序列容器中的元素按严格的线性顺序排序。各个元素按其顺序访问它们的位置。元素存储在连续的存储器位置,允许对元素进行恒定时间随机访问。可以偏移元素的指针以访问其他元素。

       使用array模板类之前需要包含#include 头文件!

                           C++数据结构——array、vector、链表_第1张图片

array a1 = {1 , 2 ,3  ,4 ,5};
    array a2 = {0 , -1 , -2 , -3 , -5};
    array  a3 = {8, 9 , 10 , 11 , 22};
    array , 3> a = {a1 , a2 , a3};


    for(int i = 0 ; i < a.size() ; i++)
    {
        for(int j = 0 ; j < a[0].size() ; j++)
            cout << a[i][j] << " ";
        cout << endl;
    }

2、array模板类的使用

(1)Iterators
      Iterators迭代器的作用是遍历array数组类中的元素。可以通过begin/end()、rbegin/rend()、cbegin/cend()、crbegin/crend()等函数进行访问。

  1. begin    返回数组的迭代头指针
  2. end    返回数组迭代的末指针
  3. rbegin    Return reverse iterator to reverse beginning
  4. rend    Return reverse iterator to reverse end
  5. cbegin    Return const_iterator to beginning
  6. cend    Return const_iterator to end
  7. crbegin    Return const_reverse_iterator to reverse beginning
  8. crend    Return const_reverse_iterator to reverse end

参考代码如下所示:

int main(void) {    
    array ad ;
    std::array arr = {1, 2, 3, 4, 5};
 
    std::cout << "array values: ";
    for (auto it = arr.begin(); it != arr.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
 
    return 0;
}

运行结果如下所示:

array values: 1 2 3 4 5

(2)Capacity

array数组容器的大小是固定的。可以通过sizeof()、size()、max_size()、empty()等函数进行检测。

1、sizeof是一个操作符(operator)。返回一个对象或类型所占的内存字节数。

语法:sizeof有三种语法形式:

  1.   sizeof (object);  //sizeof (对象)
  2.   sizeof object;   //sizeof 对象
  3.   sizeof (type_name);  //sizeof (类型)

sizeof对对象求内存大小,最终都是转换为对对象的数据类型求值。sizeof (表达式); //值为表达式的最终结果的数据类型的大小。

int i;  
sizeof(int); //值为4  
sizeof(i); //值为4,等价于sizeof(int)  
sizeof i; //值为4  
sizeof(2); //值为4,等价于sizeof(int),因为2的类型为int  
sizeof(2 + 3.14); //值为8,等价于sizeof(double),因为此表达式的结果的类型为double  

更多内容:https://www.cnblogs.com/huolong-blog/p/7587711.html 

2、size()返回数组的长度,即当前元素个数;

3、max_size()返回最大的可能的元素个数;

4、empty()检查数组是否为空,即有无元素存在。

参考代码如下所示:

int main(void) {
    std::array arr = {1, 2, 3, 4, 5}; 
    std::cout << "sizeof(array) = " << sizeof(arr) << std::endl;
    std::cout << "size of array = " << arr.size() << std::endl;
    std::cout << "max_size of array = " << arr.max_size() << std::endl;
 
    if (arr.empty()) {
        std::cout << "array is empty!" << std::endl;
    } else {
        std::cout << "array is not empty!" << std::endl;
    }
 
    return 0;
}

运行结果如下所示:

sizeof(array) = 20
size of array = 5
max_size of array = 5
array is not empty!

(3)访问方式

可以通过下标[ ]、at()、front()、back()、data()等函数访问array容器内的元素。

front 和back返回第一个和最后一个元素
data返回array的存放地址。

int main(void) {
    std::array arr = {1, 2, 3, 4, 5};
 
    std::cout << "array[0] = " << arr[0] << std::endl;
    std::cout << "array.at(4) = " << arr.at(4) << std::endl;
    std::cout << "array.front() = " << arr.front() << std::endl;
    std::cout << "array.back() = " << arr.back() << std::endl;
    std::cout << "&array: " << arr.data() << " = " << &arr << std::endl; 
    return 0;
}

运行结果如下所示:

array[0] = 1
array.at(4) = 5
array.front() = 1
array.back() = 5
&array: 0x7ffd22df6e50 = 0x7ffd22df6e50

(4)Modifiers

1、通过调用数组对象的成员函数 fill(),可以将所有元素设成给定值。例如:

values.fill(3.1415926);

2、swap()函数用于将两个array互换:

    std::array first = {1, 2, 3};
    std::array second = {6, 5, 4};
 
    first.swap(second);  // swap


first  : 1 2 3 
second : 6 5 4

swap:
first  : 6 5 4 
second : 1 2 3

(5)Compare

可以使用>  <  == 等符号对两个array数组容器进行比较。比较时采用逐个元素进行比较。

from :https://blog.csdn.net/zhengqijun_/article/details/81566109


      STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库。STL中的容器有队列容器和关联容器,容器适配器(congtainer adapters:stack,queue,priority queue),位集(bit_set),串包(string_package)等等。

(1)序列式容器(Sequence containers),每个元素都有固定位置--取决于插入时机和地点,和元素值无关,vector、deque、list;
       Vector:将元素置于一个动态数组中加以管理,可以随机存取元素(用索引直接存取),数组尾部添加或移除元素非常快速。但是在中部或头部安插元素比较费时;
       Deque:是“double-ended queue”的缩写,可以随机存取元素(用索引直接存取),数组头部和尾部添加或移除元素都非常快速。但是在中部或头部安插元素比较费时;
       List:双向链表,不提供随机存取(按顺序走到需存取的元素,O(n)),在任何位置上执行插入或删除动作都非常迅速,内部只需调整一下指针;

(2)关联式容器(Associated containers),元素位置取决于特定的排序准则,和插入顺序无关,set、multiset、map、multimap等。
       Set/Multiset:内部的元素依据其值自动排序,Set内的相同数值的元素只能出现一次,Multisets内可包含多个数值相同的元素,内部由二叉树实现,便于查找;
       Map/Multimap:Map的元素是成对的键值/实值,内部的元素依据其值自动排序,Map内的相同数值的元素只能出现一次,Multimaps内可包含多个数值相同的元素,内部由二叉树实现,便于查找;

容器类自动申请和释放内存,无需new和delete操作。

二、vector模板类

      vector 是向量类型,它可以容纳许多类型的数据,如若干个整数,所以称其为容器。看成是一种可以存放各种类型对象的容器,简单地说,vector是一个能够存放任意类型的动态数组。vector 是C++ STL的一个重要成员,使用它时需要包含头文件:

#include;

1、vector 的初始化

有五种方式,举例说明如下:

    vector a(10); //定义了10个整型元素的向量(尖括号中为元素类型名),但没有给出初值,其值是不确定的。
   vector a(10,1); //定义了10个整型元素的向量,且给出每个元素的初值为1
   vector a(b); //用b向量来创建a向量,整体复制性赋值
   vector a(b.begin(),b.begin+3); //定义了a值为b中第0个到第2个(共3个)元素
  
   int b[7]={1,2,3,4,5,9,8};
   vector a(b,b+7); //从数组中获得初值

2、访问方式

1、通过下标“[]”读取 

int a[6]={1,2,3,4,5,6};
vector b(a,a+4);
for(int i=0;i<=b.size()-1;i++)
    cout<

2、通过遍历器方式读取

int a[6]={1,2,3,4,5,6};
vector b(a,a+4);
for(vector::iterator it=b.begin();it!=b.end();it++)
    cout<<*it<<" ";

 3、访问首尾元素

a.back(); //返回a的最后一个元素
a.front(); //返回a的第一个元素

 3、添加元素push_back

1、直接将元素添加入vector

vector a;
for(int i=0;i<10;i++)
    a.push_back(i);

2、也可以从数组中选择元素,向vector中添加

int a[6]={1,2,3,4,5,6};
vector b;
for(int i=1;i<=4;i++)
    b.push_back(a[i]);

3、也可以从现有向量中选择元素向向量中添加

int a[6]={1,2,3,4,5,6};
vector b;
vector c(a,a+4);
for(vector::iterator it=c.begin();it

4、【误区】不能用下面这种做法对其进行赋值。下标只能用于获取已存在的元素,而现在的a[i]还是空的对象。

vector a;
a[i]=9;

但是可以使用这种方法对齐进行修改元素:

    vector vec;  // []
    vec.push_back(1); // [1]
    vec.push_back(2); // [1, 2]
    vec.push_back(3); // [1, 2, 3]
    vec[1] = 3; // [1, 3, 3]

4、删除元素

vec.erase(vec.begin()+2);删除第3个元素

vec.erase(vec.begin()+i,vec.end()+j);删除区间[i,j-1];区间从0开始

1、pop_back,从尾部删除

a.pop_back(); //删除a向量的最后一个元素

 2、删除a中第0个到第2个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)

a.erase(a.begin()+1,a.begin()+3); 

5、插入元素

insert() 函数有以下三种用法: 

  1. 在指定位置loc前插入值为val的元素,返回指向这个元素的迭代器,vec.insert(vec.begin()+i,a);在第i+1个元素前面插入a;
  2. 在指定位置loc前插入num个值为val的元素 
  3. 在指定位置loc前插入区间[start, end)的所有元素

1、v=2 7 9,在最前面插入一个8后,向量元素为:8 2 7 9 3

v.insert(v.begin(),8);//在最前面插入新元素。  

v.insert(v.end(),3);//在向量末尾追加新元素。

2、在a的第2个元素前插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4

a.insert(a.begin()+1,5); 

3、在a的第2个元素前,插入3个数,其值都为5

a.insert(a.begin()+1,3,5);

4、b为数组,在a的第2个元素前插入b的第3个元素到第5个元素(不包括b+6)

a.insert(a.begin()+1,b+3,b+6); 

5、vector容量

 a.size(); //返回a中元素的个数;
 a.capacity(); //返回a在内存中总共可以容纳的元素个数

  6、其他重要操作

a.assign(b.begin(), b.begin()+3); //b为向量,将b的0~2个元素构成的向量赋给a
a.assign(4,2); //是a只含4个元素,且每个元素为2
a.clear(); //清空a中的元素
a.empty(); //判断a是否为空,空则返回ture,不空则返回false
a.resize(10); //将a的现有元素个数调至10个,多则删,少则补,其值随机
a.resize(10,2); //将a的现有元素个数调至10个,多则删,少则补,其值为2
a.reserve(100); //将a的容量(capacity)扩充至100,也就是说现在测试a.capacity();的时候返回值是100.
a.swap(b); //b为向量,将a中的元素和b中的元素进行整体性交换

5、比较大小

    a==b;    //b为向量,向量的比较操作还有!=,>=,<=,>,< 

6、常用算法

使用时需要包含头文件:#include

1、对a中的从a.begin()(包括它)到a.end()(不包括它)的元素进行从小到大排列

2、对a中的从a.begin()(包括它)到a.end()(不包括它)的元素倒置,但不排列,如a中元素为1,3,2,4,倒置后为4,2,3,1

3、把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置(包括它)开始复制,覆盖掉原有元素

4、在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位置

sort(a.begin(),a.end()); 
reverse(a.begin(),a.end()); 
copy(a.begin(),a.end(),b.begin()+1); 
find(a.begin(),a.end(),10); 

三、list链表

      list 是顺序容器的一种。list 是一个双向链表。使用 list 需要包含头文件。与向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢,双向链表的每个元素中都有一个指针指向后一个元素,也有一个指针指向前一个元素。 

                                        

双向链表的节点包括:

  1. 数据域:用于存储数据元素的值。
  2. 左指针域(左链域):用于存储上一个结点地址或者说指向其直接前继结点的指针。
  3. 右指针域(右链域):用于存储下一个结点地址或者说指向其直接后继结点的指针。
 struct DNode{
     int value;
     DNode * left;
     DNode * right;
 };

      在list容器中,在已经定位到要增删元素的位置的情况下,增删元素能在常数时间内完成。在 a_{i}a_{i+1}之间插入一个元素,只需要修改a_{i}a_{i+1}中的指针即可。
                                                C++数据结构——array、vector、链表_第2张图片
                                                              图:在双向链表中插入元素


list 容器不支持根据下标随机存取元素。

1、List定义和初始化:

    listlst1;          //创建空list
    list lst2(5);       //创建含有5个元素的list
    listlst3(3,2);  //创建含有3个元素的list
    listlst4(lst2);    //使用lst2初始化lst4
    listlst5(lst2.begin(),lst2.end());  //同lst4

2、遍历List

    //迭代器法

for(list::const_iteratoriter = lst1.begin();iter != lst1.end();iter++)  
    cout<<*iter<

//

    for (it = mylist.begin(); it != mylist.end(); ++i)  
        cout << *it << " "; 

3、push_back() 和push_front():

   使用list的成员函数push_back和push_front插入一个元素到list中。其中push_back()从list的末端插入,而 push_front()实现的从list的头部插入

listOne.push_front(3);  
listOne.push_front(2);  
listOne.push_front(1);  
listOne.push_back(4);  
listOne.push_back(5);  
listOne.push_back(6);
for(list::iterator it=listOne.begin();it!=listOne.end();++it)
    cout<<*it<<" ";

输出:1 2 3 4 5 6

4、 empty():利用empty() 判断list是否为空。

5、 resize():调用resize(n)将list的长度改为只容纳n个元素,超出的元素将被删除。

6 clear(): 清空list中的所有元素。

7 front()和back(): 

     通过front()可以获得list容器中的头部元素,通过back()可以获得list容器的最后一个元素。但是有一点要注意,就是list中元素是空的时候,调用front()和back()并不报错,那我们编程序时就要注意了,个人觉得在使用之前最好先调用empty()函数判断list是否为空。

8 pop_back和pop_front():

     通过pop_back()删除最后一个元素,通过pop_front()删除第一个元素;序列必须不为空,如果当list为空的时候调用pop_back()和pop_front()会使程序崩掉。

9 assign()

    具体和vector中的操作类似,也是有两种情况,第一种是:l1.assign(n,val)将 l1中元素变为n个T(val)。第二种情况是:l1.assign(l2.begin(),l2.end())将l2中的从l2.begin()到l2.end()之间的数值赋值给l1。

10 swap():交换两个链表(两个重载),一个是l1.swap(l2); 另外一个是swap(l1,l2),都可能完成连个链表的交换。

11 reverse():通过reverse()完成list的逆置。

12 merge():

   合并两个链表并使之默认升序(也可改),l1.merge(l2,greater()); 调用结束后l2变为空,l1中元素包含原来l1 和 l2中的元素,并且排好序,升序。其实默认是升序,greater()可以省略,另外greater()是可以变的,也可以不按升序排列。

13 insert():在指定位置插入一个或多个元素(三个重载):

  1. l1.insert(l1.begin(),100); 在l1的开始位置插入100。
  2. l1.insert(l1.begin(),2,200); 在l1的开始位置插入2个100。
  3. l1.insert(l1.begin(),l2.begin(),l2.end());在l1的开始位置插入l2的从开始到结束的所有位置的元素。

14 erase():删除一个元素或一个区域的元素(两个重载)

  1. l1.erase(l1.begin()); 将l1的第一个元素删除。
  2. l1.erase(l1.begin(),l1.end()); 将l1的从begin()到end()之间的元素删除。

from:https://blog.csdn.net/u011630575/article/details/79734358

from:https://blog.csdn.net/Richard__Ting/article/details/79490665


单链表node的数据结构定义如下:

class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
        val = x;
        next = null;
    }
}

单链表的反转是常见的面试题目:

思路:把当前链表的下一个节点pCur插入到头结点dummy的下一个节点中,就地反转。

dummy->1->2->3->4->5:

dummy->2->1->3->4->5

dummy->3->2->1->4->5

dummy->4>-3->2->1->5

dummy->5->4->3->2->1

过程

pCur:需要反转的节点。

循环条件:pCur is not null

  1. prev连接下一次需要反转的节点
  2. 反转节点pCur
  3. 纠正头结点dummy的指向
  4. pCur指向下一次要反转的节点
prev.next = pCur.next;
pCur.next = dummy.next;
dummy.next = pCur;
pCur = prev.next;

     C++数据结构——array、vector、链表_第3张图片

C++数据结构——array、vector、链表_第4张图片

。。。。

C++数据结构——array、vector、链表_第5张图片

// 1.就地反转法
    public ListNode reverseList1(ListNode head) {
        if (head == null)
            return head;
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode prev = dummy.next;
        ListNode pCur = prev.next;
        while (pCur != null) {
            prev.next = pCur.next;
            pCur.next = dummy.next;
            dummy.next = pCur;
            pCur = prev.next;
        }
        return dummy.next;
    }

from: https://www.cnblogs.com/byrhuangqiang/p/4311336.html

更多参见Python学习:https://blog.csdn.net/qq_30815237/article/details/90750349

 

 

你可能感兴趣的:(C++学习)