#数据结构:顺序表

1数据结构:线性表

1、顺序线性表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJAKKykT-1689938969263)(https://github.com/EZreal-Map/DataStructure/blob/main/image/1689772680273.png)]

1.1、线性表主函数测试文件

#include <iostream>
#include "SeqList.h"

using namespace std;


int main(){
	//1、顺序线性表
    SeqList<int> mylist({ 1,2,3,4,5 });
    mylist.display();
        /*顺序线性表长度为[5], 其中第[1]个元素为:1
        顺序线性表长度为[5], 其中第[2]个元素为:2
        顺序线性表长度为[5], 其中第[3]个元素为:3
        顺序线性表长度为[5], 其中第[4]个元素为:4
        顺序线性表长度为[5], 其中第[5]个元素为:5*/
    // 获取元素
    cout << "获取元素:" << mylist.get(3) << endl << endl;
        /*获取元素:3*/
    // 插入元素
    mylist.insert(2, 100);
    mylist.display();
        /*顺序线性表长度为[6], 其中第[1]个元素为:1
        顺序线性表长度为[6], 其中第[2]个元素为:100
        顺序线性表长度为[6], 其中第[3]个元素为:2
        顺序线性表长度为[6], 其中第[4]个元素为:3
        顺序线性表长度为[6], 其中第[5]个元素为:4
        顺序线性表长度为[6], 其中第[6]个元素为:5*/
    // 删除元素
    mylist.del(5);
    mylist.display();
        /*顺序线性表长度为[5], 其中第[1]个元素为:1
        顺序线性表长度为[5], 其中第[2]个元素为:100
        顺序线性表长度为[5], 其中第[3]个元素为:2
        顺序线性表长度为[5], 其中第[4]个元素为:3
        顺序线性表长度为[5], 其中第[5]个元素为:5*/
}

1.2、线性表类(class SeqList)声明与定义头文件——SeqLis.h

// SeqList.cpp
#ifndef SEQLIST_H
#define SEQLIST_H
#include <iostream>
using namespace std;
const int MAXSIZE = 100;

template<typename ElemType>
class SeqList {
private:
	ElemType datas[MAXSIZE]; /*数组 存储数据元素*/
	int length;				 /*线性表当前长度*/
public:
	/*构造函数*/
	SeqList(std::initializer_list<ElemType> datas);
	/*1、获取元素*/
	ElemType get(int num);

	/*2、插入元素*/
	void insert(int num, ElemType data);

	/*3、 删除元素*/
	void del(int num);

	/*4、 遍历显示元素*/
	void display();
};


template<typename ElemType>
SeqList<ElemType>::SeqList(std::initializer_list<ElemType> datas) {
	/*std::initializer_list 是 C++ 中的一个模板类,
		它提供了一种方便的方式来创建初始化列表,允许在构造函数或函数参数中传递一组值。*/
	this->length = 0;
	for (ElemType data : datas) {
		this->datas[this->length++] = data;
	}
}
/*1、获取元素*/
template<typename ElemType>
ElemType SeqList<ElemType>::get(int num) {
	if (num<1 || num>this->length) {
		cout << "超过线性表" << endl;
		return 0;
	}
	else {
		return this->datas[num - 1];
	}

}

/*2、插入元素*/
template<typename ElemType>
void SeqList<ElemType>::insert(int num, ElemType data) {
	if (num<1 || num>this->length + 1) {
		cout << "超过线性表" << endl;
	}
	else {
		++(this->length);
		--num;
		for (int i = this->length; i > num; --i) {
			this->datas[i] = this->datas[i - 1];
		}
		this->datas[num] = data;
	}
}

/*3、 删除元素*/
template<typename ElemType>
void SeqList<ElemType>::del(int num) {
	if (num<1 || num>this->length) {
		cout << "超过线性表" << endl;
	}
	else {
		--(this->length);
		--num;
		for (int i = num; i < this->length; ++i) {
			this->datas[i] = this->datas[i + 1];
		}
	}
}

/*4、 遍历显示元素*/
template<typename ElemType>
void SeqList<ElemType>::display() {
	for (int i = 0; i < this->length; ++i) {
		cout << "顺序线性表长度为[" << this->length << "], 其中第[" << i + 1 << "]个元素为:" << datas[i] << endl;
	}
	cout << endl;
}
#endif // SEQLIST_H

补充:[C++模板的声明和定义为什么不能分写在.h与.cpp文件中?](url https://www.cnblogs.com/JCpeng/p/15072692.html)

将模板的声明和定义都放置在同一个.h文件中。这同时也是为什么所有的STL头文件都包含模板定义的原因。

1.3 使用C++标准库容器vector的动态数组功能

1.3.1 定义和初始化vector对象
语法功能
vector v1v1是一个空vector,它潜在的元素是T类型,执行默认初始化
vector v2(v1)v2中包含v1所以元素的副本
vector v2 = v1等价于v2(v1),v2中包含v1所以元素的副本
vector v3(n, val)v3包含了n个重复的元素,每个元素的值都是val
vector v4(n)v4包含了n个重复地执行了值初始化的对象
vector v5{a,b,c…}v5包含了初始化个数的元素,每个元素被赋予相应的初始值
vector v5={a,b,c…}等价于v5{a,b,c…}
1.3.2 vector的操作
方法功能
v.empty()如果v不含有任何元素,返回真;否则返回假
v.size()返回v中元素的个数
v.push_back(t)向v的尾端添加一个值为t的元素
v.pop_back()在末尾弹出一个元素
v[n]返回v中第n个位置上元素的引用
v1 = v2用v2中元素的拷贝替换v1中的元素
v1 = {a,b,c…}用列表中元素的拷贝替换v1中的元素(std::initializer_list)
v1 == v2v1和v2相等当且仅当它们的元素数量相同且对应位置的元素值都相同
v1 != v2
<, <=, >, >=顾明思义,以字典顺序进行比较
1.3.3 标准容器迭代器的运算符
语法功能
vector::iterator it_b = v.begin();begin成员负责返回指向第一个元素的迭代器
vector::iterator it_e = v.end();end成员 最后一个元素
*iter返回迭代器iter所指元素的引用
iter->mem解引用iter并获取该元素的名位mem的成员,等价与(*iter).mem
++iter令iter指示容器中的下一个元素
–iter上一个元素
iter+n迭代器加上一个整数仍得一个迭代器,迭代器指示的新位置与原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指向指示容器尾元素的下一个位置
iter-n
iter+=niter = iter+ n;
iter1-iter2两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后将得到左侧的迭代器
>、>=、<、<=比较前后iter1、iter2

2、线性表的链式存储——单链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s0gqixMw-1689938969263)(https://github.com/EZreal-Map/DataStructure/blob/main/image/带有头结点的单链表)]

#include <iostream>
using namespace std;

template<typename Elemtype>
class Node {
public:
    Elemtype data;        // 存放数据
    Node<Elemtype>* next; // 定义一个指向Node数据类型的指针
    
    Node() :next(nullptr) {} //创建头结点的时候,没有参数,需要一个data为空的构造函数
    Node(Elemtype data) :data(data), next(nullptr) {} // Node的构造函数
};

template<typename Elemtype>
class LinkList {
private:
    Node<Elemtype>* head;
public:
    // Elemtype构造函数 
    // 这种是不带头结点的LinkList结构
    // LinkList() :head(nullptr) {} 
    // 带头结点的LinkList结构
    LinkList() {
        this->head = new Node<Elemtype>();
    }
    ~LinkList() {
        Node<Elemtype>* ptr = head,*temp;
        while (ptr) {
            temp = ptr;
            ptr = ptr->next;
            delete(temp);
        }
    }
    // 1、链表的增加
    void append(Elemtype value) {
        Node<Elemtype>* newNode = new Node<Elemtype>(value);
        Node<Elemtype>* ptr = head;
        while (ptr->next!=nullptr) {
            // 这里使用(*ptr).next更好理解,
            // 先把指针解引用,得到Node,然后取得Node.next
            ptr = ptr->next; // 前进
        }
        // 找到ptr->next 为nullptr后,就是找到链表的末尾
        ptr->next = newNode;
    }

    // 2.1、链表的插入
    void insert(int num,Elemtype value) {
        Node<Elemtype>* ptr = head;
        for (int i = 0; i < num-1; ++i) {
            ptr = ptr->next;
            if (ptr->next == nullptr) // 检查超过边界,在单链表最后插入一个元素
            {break;}
        }        
        Node<Elemtype>* temp = ptr->next;
        ptr->next = new Node<Elemtype>(value);
        ptr = ptr->next;
        ptr->next = temp;
    }

    //2.2、 重载链表的插入,末位插入 和1、链表的增加 append方法一样 (可删除)
    void insert(Elemtype value) {
        Node<Elemtype>* ptr = head;
        while (ptr->next != nullptr) {
            ptr = ptr->next;
        }
        ptr->next = new Node<Elemtype>(value);
    }

    // 3.1、链表的删除
    void remove(int num) {
        Node<Elemtype>* ptr = head;
        for (int i = 0; i < num - 1; ++i) {
            ptr = ptr->next;
            if (ptr == nullptr) // 检查超过边界
            {
                cout << "删除链表的第["<< num <<"]个元素失败,链表一共有["<< i <<"]个元素" << endl;
                return;
            }
        }
        Node<Elemtype>* temp = ptr->next;
        ptr->next = temp->next;
        delete(temp); // 别忘了跳过要删除的Node后,清空Node
    }

    // 3.2、重载链表的末位删除
    // 问题出在你只删除了节点本身,但没有更新倒数第二个节点的next指针。
    // 这样,倒数第二个节点仍然指向被删除的末位节点,导致无法正确遍历链表,
    // 且被删除的节点内存没有被释放。 

    // 虽然通过 while (ptr->next != nullptr) 找到末尾,
    // 此时ptr与导数第二个Node.next是指向相同的地址的2份拷贝。
    void remove() {
        Node<Elemtype>* ptr = head;
        Node<Elemtype>* prev = nullptr;
        while (ptr->next != nullptr) {
            prev = ptr; // 增加记录上一个结点
            ptr = ptr->next;
        }

        // 现在ptr指向末位节点,prev指向倒数第二个节点
        if (prev != nullptr) {
            delete(prev->next);
            prev->next = nullptr;
        }
        else {
            // 如果链表为空或只有一个节点
            // 删除头结点
            // delete head->next;
            // head->next = nullptr;
        }
    }

    // 4、链表的显示
    void display() {
        Node<Elemtype>* ptr = head;
        int i = 1;
        while (ptr->next != nullptr) {
            ptr = ptr->next;
            cout << "单链表的第[" << i << "]个元素为:" << ptr->data << endl;
            ++i;
        }
        cout << endl;
    }
};


int main()
{
    LinkList<int> List;
    List.append(10);
    List.append(20);
    List.append(30);
    List.append(40);
    List.display();
    List.insert(3, 100);
    List.insert(3, 200);
    List.insert(30, 1000);
    List.insert(10000);
    List.display();
    List.remove(1);
    List.remove(6);
    List.remove();
    List.remove();
    List.display();
}

2.1、单链表主函数测试文件

#include <iostream>
#include "LinkList.h"
using namespace std;

int main()
{
    LinkList<int> List;
    List.append(10);
    List.append(20);
    List.append(30);
    List.append(40);
    List[2] = 200;
    List.display();
        /*单链表的第[1]个元素为:10
        单链表的第[2]个元素为:200
        单链表的第[3]个元素为:30
        单链表的第[4]个元素为:40*/
    List.insert(3, 100);
    List.insert(3, 200);
    List.insert(30, 1000);
    List.insert(10000);
    List.display();
        /*单链表的第[1]个元素为:10
        单链表的第[2]个元素为:200
        单链表的第[3]个元素为:200
        单链表的第[4]个元素为:100
        单链表的第[5]个元素为:30
        单链表的第[6]个元素为:40
        单链表的第[7]个元素为:1000
        单链表的第[8]个元素为:10000*/
    List.remove(1);
    List.remove(6);
    List.remove();
    List.remove();
    List[4] = 3000;
    List.display();
        /*单链表的第[1]个元素为:200
        单链表的第[2]个元素为:200
        单链表的第[3]个元素为:100
        单链表的第[4]个元素为:3000*/
}

2.2、单链表类(class LinkList) 结点类(class Node)声明与定义头文件——LinkList.h

#pragma once
#include <iostream>
using namespace std;

//结点类(class Node)声明
template<typename Elemtype>
class Node {
public:
    Elemtype data;        // 存放数据
    Node<Elemtype>* next; // 定义一个指向Node数据类型的指针

    Node() :next(nullptr) {} //创建头结点的时候,没有参数,需要一个data为空的构造函数
    Node(Elemtype data) :data(data), next(nullptr) {} // Node的构造函数
};

//单链表类(class LinkList)声明
template<typename Elemtype>
class LinkList {
private:
    Node<Elemtype>* head;
public:
    // Elemtype构造函数 
    // 这种是不带头结点的LinkList结构
    // LinkList() :head(nullptr) {} 
    // 带头结点的LinkList结构
    LinkList() {
        this->head = new Node<Elemtype>();
    }
    ~LinkList() {
        Node<Elemtype>* ptr = head, * temp;
        while (ptr) {
            temp = ptr;
            ptr = ptr->next;
            delete(temp);
        }
    }
    // 1、链表的增加
    void append(Elemtype value);

    // 2.1、链表的插入
    void insert(int num, Elemtype value);

    //2.2、 重载链表的插入,末位插入 和1、链表的增加 append方法一样 (可删除)
    void insert(Elemtype value);
     
    // 3.1、链表的删除
    void remove(int num);

    // 3.2、重载链表的末位删除
    void remove();
    // 4、链表查或改 运算符operator[]重载
    Elemtype& operator[](int num);

    // 5、链表的显示
    void display();
};


// 1、链表的增加
template<typename Elemtype>
void LinkList<Elemtype>::append(Elemtype value) {
    Node<Elemtype>* newNode = new Node<Elemtype>(value);
    Node<Elemtype>* ptr = head;
    while (ptr->next != nullptr) {
        // 这里使用(*ptr).next更好理解,
        // 先把指针解引用,得到Node,然后取得Node.next
        ptr = ptr->next; // 前进
    }
    // 找到ptr->next 为nullptr后,就是找到链表的末尾
    ptr->next = newNode;
}

// 2.1、链表的插入
template<typename Elemtype>
void LinkList<Elemtype>::insert(int num, Elemtype value) {
    Node<Elemtype>* ptr = head;
    for (int i = 0; i < num - 1; ++i) {
        ptr = ptr->next;
        if (ptr->next == nullptr) // 检查超过边界,在单链表最后插入一个元素
        {
            break;
        }
    }
    Node<Elemtype>* temp = ptr->next;
    ptr->next = new Node<Elemtype>(value);
    ptr = ptr->next;
    ptr->next = temp;
}

//2.2、 重载链表的插入,末位插入 和1、链表的增加 append方法一样 (可删除)
template<typename Elemtype>
void LinkList<Elemtype>::insert(Elemtype value) {
    Node<Elemtype>* ptr = head;
    while (ptr->next != nullptr) {
        ptr = ptr->next;
    }
    ptr->next = new Node<Elemtype>(value);
}

// 3.1、链表的删除
template<typename Elemtype>
void LinkList<Elemtype>::remove(int num) {
    Node<Elemtype>* ptr = head;
    for (int i = 0; i < num - 1; ++i) {
        ptr = ptr->next;
        if (ptr == nullptr) // 检查超过边界
        {
            cout << "删除链表的第[" << num << "]个元素失败,链表一共有[" << i << "]个元素" << endl;
            return;
        }
    }
    Node<Elemtype>* temp = ptr->next;
    ptr->next = temp->next;
    delete(temp); // 别忘了跳过要删除的Node后,清空Node
}

// 3.2、重载链表的末位删除
// 问题出在你只删除了节点本身,但没有更新倒数第二个节点的next指针。
// 这样,倒数第二个节点仍然指向被删除的末位节点,导致无法正确遍历链表,
// 且被删除的节点内存没有被释放。 
// 虽然通过 while (ptr->next != nullptr) 找到末尾,
// 此时ptr与导数第二个Node.next是指向相同的地址的2份拷贝。
template<typename Elemtype>
void LinkList<Elemtype>::remove() {
    Node<Elemtype>* ptr = head;
    Node<Elemtype>* prev = nullptr;
    while (ptr->next != nullptr) {
        prev = ptr; // 增加记录上一个结点
        ptr = ptr->next;
    }

    // 现在ptr指向末位节点,prev指向倒数第二个节点
    if (prev != nullptr) {
        delete(prev->next);
        prev->next = nullptr;
    }
    else {
        // 如果链表为空或只有一个节点
        // 删除头结点
        // delete head->next;
        // head->next = nullptr;
    }
}

// 4、链表查或改 运算符operator[]重载
template<typename Elemtype>
Elemtype& LinkList<Elemtype>::operator[](int num) {
    Node<Elemtype>* ptr = head;
    for (int i = 0; i < num; ++i) {
        ptr = ptr->next;
    }
    return ptr->data;
}

// 5、链表的显示
template<typename Elemtype>
void LinkList<Elemtype>::display() {
    Node<Elemtype>* ptr = head;
    int i = 1;
    while (ptr->next != nullptr) {
        ptr = ptr->next;
        cout << "单链表的第[" << i << "]个元素为:" << ptr->data << endl;
        ++i;
    }
    cout << endl;
}

tr;
}
}

// 4、链表查或改 运算符operator[]重载
template
Elemtype& LinkList::operator[](int num) {
Node* ptr = head;
for (int i = 0; i < num; ++i) {
ptr = ptr->next;
}
return ptr->data;
}

// 5、链表的显示
template
void LinkList::display() {
Node* ptr = head;
int i = 1;
while (ptr->next != nullptr) {
ptr = ptr->next;
cout << “单链表的第[” << i << “]个元素为:” << ptr->data << endl;
++i;
}
cout << endl;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值