【数据结构】|实验4_链式描述线性表


笔记|第六章:链式描述

A:链表实现

题目

要求

  1. 封装链表类,链表迭代器类
  2. 链表类需提供操作:在指定位置插入元素,删除指定元素,搜索链表中是否有指定元素,原地逆置链表,输出链表
  3. 不得使用与链表实现相关的STL

描述

第一行两个整数 N 和 Q。
第二行 N 个整数,作为节点的元素值,创建链表。
接下来 Q 行,执行各个操作,具体格式如下:

  • 插入操作 : 1 idx val,在链表的 idx 位置插入元素 val;
  • 删除操作 : 2 val,删除链表中的 val 元素。若链表中存在多个该元素,仅删除第一个。若该元素不存在,输出 -1
  • 逆置操作 : 3,原地逆置链表;
  • 查询操作 : 4 val,查询链表中的 val 元素,并输出其索引。若链表中存在多个该元素,仅输出第一个的索引。若不存在该元素,输出 -1
  • 输出操作 : 5,使用 链表迭代器 ,输出当前链表索引与元素的异或和: f ( chain ) = ∑ i = 0 n − 1 i ⊕ chain [ i ] , n = len(chain) f(\text{chain}) = \sum_{i=0}^{n-1}i\oplus \text{chain}[i], n=\text{len(chain)} f(chain)=i=0n1ichain[i],n=len(chain)

样例 1
Input

10 10
6863 35084 11427 53377 34937 14116 5000 49692 70281 73704 
4 6863
1 2 44199
5
4 21466
1 6 11483
5
4 34937
5
4 6863
1 10 18635

Output

0
398665
-1
410141
5
410141
0

限制
1s
数据范围
在这里插入图片描述

思路

  • 需要封装链表类、链表迭代器类 (基本代码在笔记中有)
    • 结构体chainNode:来定义节点的数据类型
      • 数据域element、链域*next
      • 构造函数
    • chain:实现关于链表的各种操作
      • 构造函数:头节点置空、长度为0
      • 复制构造函数:首先复制链表长度,分两种情况
        • 链表为空:头节点NULL
        • 链表不为空:先初始化第一个节点,然后利用循环逐步复制下一个节点
      • 析构函数:利用循环不断删除头节点
    • 链表迭代器类:作为类chain的成员类
//非空
    //初始化第一个节点
    chainNode<T>* sourceNode = lis.firstNode;//要复制的节点
    firstNode = new chainNode<T>(sourceNode->element);//要复制的首元素
    sourceNode = sourceNode->next;//移向要复制额下一个节点
    chainNode<T>* targetNode = firstNode;//当前链表的最后一个节点
    //移向要复制的下一个节点
    sourceNode = sourceNode->next;
    while (sourceNode != NULL) {
        targetNode->next = new chainNode<T>(sourceNode->element);//复制
        targetNode = targetNode->next;//后移
        sourceNode = sourceNode->next;
    }
    targetNode->next = NULL;//链表结束,置成空指针

关于链表类提供的操作

  1. 查询indexOf:参数theElement
  • 从头节点开始,依次往后搜索,并用变量标记当前查询到的位置
  • 终止条件:指针指向表尾(搜索指针为空currentNode==NULL) or 找到元素(不为空)
  • 判断属于哪种情况,返回对应的数
  1. 插入insert:idx,val
  • 两种情况:在表头插入、在表中间插入
  • 表头插入:更新头节点firstNode = new chainNode<T>(theElement, firstNode);
  • 表中间插入:
    • 找到新元素的前驱节点
    • 让前驱节点的next指向新元素
  • 链表长度+1
  1. 删除erase
  • 两种情况:删除头节点、删除其他节点
  • 删除头节点:头节点指向头结点的下一个节点
  • 删除其他节点:
    • 找到前驱节点
    • deleteNode=p->next
    • 让前驱节点的下一个节点,指向删除元素后面的节点
  • 删除deleteNode,链表长度-1
  1. 输出Output:利用链表迭代器,按照公式计算并返回
  2. 逆置reverse

在这里插入图片描述

  1. 主函数:在删除部分要注意判断返回的是否是-1

AC代码

#include<iostream>
using namespace std;
//结构chainNode
template<class T>
struct chainNode {
    //数据成员
    T element;//节点的数据域,存储元素
    chainNode<T>* next;//链域,存储下一个节点的指针

    chainNode() {};
    chainNode(const T& e) :element(e) {}
    chainNode(const T& e, chainNode<T>* n) :element(e), next(n) {}
};
//chain链表类
template<class T>
class chain
{
public:
    //链表的成员类iterator
    class iterator {
    public:
        //构造函数 
        iterator(chainNode<T>* theNode = NULL)
        {
            node = theNode;
        }

        //解引用操作符 
        T& operator*() const { return node->element; }
        T* operator->() const { return &node->element; }

        //迭代器加法操作 
        iterator& operator++()
        {
            node = node->next; 
            return *this;
        }//前加 
        iterator operator++(int)
        {
            iterator old = *this; 
            node = node->next; 
            return old;
        }//后加 

        //相等检验 
        bool operator != (const iterator right) const
        {
            return node != right.node;
        }
        bool operator == (const iterator right) const
        {
            return node == right.node;
        }
    protected:
        chainNode<T>* node;//指向表节点的指针
    };

    //构造函数、复制构造函数、析构函数
    chain();
    chain(const chain<T>& lis);
    ~chain();

    iterator begin() { return iterator(firstNode); }
    iterator end() { return iterator(NULL); }

    int indexOf(const T& theElement) const;//查询
    void insert(int theIndex, const T& theElement);//插入
    void erase(int theIndex);//删除
    void reverse();//逆置
    int Output();//输出
protected:
    //指向链表中第一个节点的指针
    chainNode<T>* firstNode;
    //线性表的元素个数
    int listSize;
};
//构造函数
template<class T>
chain<T>::chain() {
    firstNode = NULL;
    listSize = 0;
}
//复制构造函数
template<class T>
chain<T>::chain(const chain<T>& lis) {
    listSize = lis.listSize;
    //lis为空
    if (listSize == 0) {
        firstNode = NULL;
        return;
    }
    //非空
    //初始化第一个节点
    chainNode<T>* sourceNode = lis.firstNode;//要复制的节点
    firstNode = new chainNode<T>(sourceNode->element);//要复制的首元素
    sourceNode = sourceNode->next;//移向要复制额下一个节点
    chainNode<T>* targetNode = firstNode;//当前链表的最后一个节点
    //移向要复制的下一个节点
    sourceNode = sourceNode->next;
    while (sourceNode != NULL) {
        targetNode->next = new chainNode<T>(sourceNode->element);//复制
        targetNode = targetNode->next;//后移
        sourceNode = sourceNode->next;
    }
    targetNode->next = NULL;//链表结束,置成空指针
}
//析构函数
template<class T>
chain<T>::~chain() {
    while (firstNode) {
        chainNode<T>* nextNode = firstNode->next;
        delete firstNode;
        firstNode = nextNode;
    }
}
//查询
template<class T>
int chain<T>::indexOf(const T& theElement) const {
    //从firstNode开始
    chainNode<T>* currentNode = firstNode;
    int index = 0;
    //指向表尾或找到元素
    while (currentNode != NULL && currentNode->element != theElement) {
        currentNode = currentNode->next;
        index++;
    }
    //判断属于哪种情况
    if (currentNode == NULL)
        return -1;
    else
        return index;
}
//插入
template<class T>
void chain<T>::insert(int theIndex, const T& theElement) {
    //在表头插入
    if (theIndex == 0)
        firstNode = new chainNode<T>(theElement, firstNode);
    else {
        chainNode<T>* p = firstNode;
        for (int i = 0; i < theIndex - 1; i++)
            p = p->next;//找到新元素的前驱
        p->next = new chainNode<T>(theElement, p->next);
    }
    listSize++;
}
//删除
template<class T>
void chain<T>::erase(int theIndex) {
    chainNode<T>* deleteNode = firstNode;
    //删除头节点
    if (theIndex == 0) {
        deleteNode = firstNode;
        firstNode = firstNode->next;
    }
    else {
        chainNode<T>* p = firstNode;
        //找到前驱节点
        for (int i = 0; i < theIndex - 1; i++) {
            p = p->next;
        }
        deleteNode = p->next;
        p->next = p->next->next;
    }
    listSize--;
    delete deleteNode;
}
//逆置
template<class T>
void chain<T>::reverse() {
    if (firstNode == NULL || firstNode->next == NULL) return;
    chainNode<T>* pastNode = firstNode;//用来遍历原链表的指针
    chainNode<T>* currentNode = pastNode->next;//遍历原链表指针时,用来保存pastNode下一个节点的指针
    chainNode<T>* nextNode = currentNode->next;
    firstNode->next = NULL;
    while (nextNode != NULL) {
        currentNode->next = pastNode;
        pastNode = currentNode;
        currentNode = nextNode;
        nextNode = nextNode->next;
    }
    currentNode->next = pastNode;
    firstNode = currentNode;
}
//输出
template<class T>
int chain<T>::Output() {
    int count = 0, index = 0;
    iterator beginning = begin();
    while (index < listSize) {
        count += index ^ (*beginning);
        beginning++;
        index++;
    }
    return count;
}

int main() {
    int N, Q;
    cin >> N >> Q;
    int* a = new int[N];
    for (int i = 0; i < N; i++)
        cin >> a[i];
    chain<int>List;
    for (int i = 0; i < N; i++)
        List.insert(i, a[i]);
    delete[]a;
    for (int i = 0; i < Q; i++) {
        int choice, idx, val, index;
        cin >> choice;
        switch (choice)
        {
        case 1:
            cin >> idx >>val;
            List.insert(idx, val);
            break;
        case 2:
            cin >> val;
            index = List.indexOf(val);
            if (index != -1) List.erase(index);
            else cout << "-1" << endl;
            break;
        case 3:
            List.reverse();
            break;
        case 4:
            cin >> val;
            cout << List.indexOf(val) << endl;
            break;
        case 5:
            cout << List.Output() << endl;
            break;
        default:
            break;
        }
    }
    return 0;
}

B:链表合并

题目

要求

  1. 使用题目 链表实现 中实现的链表类、迭代器类完成本题
  2. 不得使用与题目实现相关的STL

描述

给定两组整数序列,你需要分别创建两个有序链表,使用链表迭代器实现链表的合并,并分别输出这三个有序链表的索引与元素的异或和。

Note: 给定序列是无序的,你需要首先得到一个有序的链表

格式

输入

第一行两个整数 N 和 M。
第二行 N 个整数,代表第一组整数序列。
第三行 M 个整数,代表第二组整数序列。

输出

三行整数。分别代表第一组数、第二组数对应的有序链表与合并后有序链表的索引与元素的异或和。

链表异或和的计算方式如下:

f ( chain ) = ∑ i = 0 n − 1 i ⊕ chain [ i ] , n = len(chain) f(\text{chain})=\sum_{i=0}^{n-1} i\oplus \text{chain}[i], n=\text{len(chain)} f(chain)=i=0n1ichain[i],n=len(chain)

样例 1
输入

3 0
3 1 2

输出

5
0
5

限制
1s
数据范围

在这里插入图片描述

思路

  • 链表类、迭代器类等与A题相同,不做赘述
  • 增加排序函数、合并链表的函数
  • 排序函数:有两种方法 (这里选择的第二种)
    • 在输入的时候先将序列用数组存储,并进行排序,然后将有序序列进行插入链表
    • 在输入的时候直接插入链表,然后在链表内进行排序操作
      • 可选排序函数很多(一开始用的桶排序但是运行失败了)
      • 冒泡排序bubbleSort:利用两个相邻的节点进行比较,并逐步后移
  • 合并merge:对有序序列进行合并(双指针法)
    • 两个迭代器,用于遍历两个链表;一个计数器,用来记录合并后链表中的位置
    • 由于链表长度不清楚,因此可能会出现一个链表已经遍历完,另一个链表还没有遍历完的情况。对于没遍历完的链表,直接插入到链表末尾即可
    • 当两个链表都没遍历完时:比较当前元素大小,并将小的元素先插入,插入的元素所属链表的迭代器要后移,未插入的不变。(只移动取值的指针)
    • 当至少有一个链表已经遍历完成,判断是否有没遍历完的链表,直接插入到表尾。

AC代码

#include<iostream>
using namespace std;
//结构chainNode
template<class T>
struct chainNode {
    //数据成员
    T element;//节点的数据域,存储元素
    chainNode<T>* next;//链域,存储下一个节点的指针

    chainNode() {};
    chainNode(const T& e) :element(e) {}
    chainNode(const T& e, chainNode<T>* n) :element(e), next(n) {}
};
//chain链表类
template<class T>
class chain
{
public:
    //链表的成员类iterator
    class iterator {
    public:
        //构造函数 
        iterator(chainNode<T>* theNode = NULL)
        {
            node = theNode;
        }

        //解引用操作符 
        T& operator*() const { return node->element; }
        T* operator->() const { return &node->element; }

        //迭代器加法操作 
        iterator& operator++()
        {
            node = node->next;
            return *this;
        }//前加 
        iterator operator++(int)
        {
            iterator old = *this;
            node = node->next;
            return old;
        }//后加 

        //相等检验 
        bool operator != (const iterator right) const
        {
            return node != right.node;
        }
        bool operator == (const iterator right) const
        {
            return node == right.node;
        }
    protected:
        chainNode<T>* node;//指向表节点的指针
    };

    //构造函数、复制构造函数、析构函数
    chain(int initial);
    chain(const chain<T>& lis);
    ~chain();

    iterator begin() { return iterator(firstNode); }
    iterator end() { return iterator(NULL); }

    int indexOf(const T& theElement) const;//查询
    void insert(int theIndex, const T& theElement);//插入
    void erase(int theIndex);//删除
    void reverse();//逆置
    int Output();//输出

    void bubbleSort();//冒泡排序
    void merge(chain<T>& a, chain<T>& b);//合并
    
protected:
    //指向链表中第一个节点的指针
    chainNode<T>* firstNode;
    //线性表的元素个数
    int listSize;
};
//构造函数
template<class T>
chain<T>::chain(int initial) {
    firstNode = NULL;
    listSize = 0;
}
//复制构造函数
template<class T>
chain<T>::chain(const chain<T>& lis) {
    listSize = lis.listSize;
    //lis为空
    if (listSize == 0) {
        firstNode = NULL;
        return;
    }
    //非空
    //初始化第一个节点
    chainNode<T>* sourceNode = lis.firstNode;//要复制的节点
    firstNode = new chainNode<T>(sourceNode->element);//要复制的首元素
    sourceNode = sourceNode->next;//移向要复制额下一个节点
    chainNode<T>* targetNode = firstNode;//当前链表的最后一个节点
    //移向要复制的下一个节点
    sourceNode = sourceNode->next;
    while (sourceNode != NULL) {
        targetNode->next = new chainNode<T>(sourceNode->element);//复制
        targetNode = targetNode->next;//后移
        sourceNode = sourceNode->next;
    }
    targetNode->next = NULL;//链表结束,置成空指针
}
//析构函数
template<class T>
chain<T>::~chain() {
    while (firstNode) {
        chainNode<T>* nextNode = firstNode->next;
        delete firstNode;
        firstNode = nextNode;
    }
}
//查询
template<class T>
int chain<T>::indexOf(const T& theElement) const {
    //从firstNode开始
    chainNode<T>* currentNode = firstNode;
    int index = 0;
    //指向表尾或找到元素
    while (currentNode != NULL && currentNode->element != theElement) {
        currentNode = currentNode->next;
        index++;
    }
    //判断属于哪种情况
    if (currentNode == NULL)
        return -1;
    else
        return index;
}
//插入
template<class T>
void chain<T>::insert(int theIndex, const T& theElement) {
    //在表头插入
    if (theIndex == 0)
        firstNode = new chainNode<T>(theElement, firstNode);
    else {
        chainNode<T>* p = firstNode;
        for (int i = 0; i < theIndex - 1; i++)
            p = p->next;//找到新元素的前驱
        p->next = new chainNode<T>(theElement, p->next);
    }
    listSize++;
}
//删除
template<class T>
void chain<T>::erase(int theIndex) {
    chainNode<T>* deleteNode = firstNode;
    //删除头节点
    if (theIndex == 0) {
        deleteNode = firstNode;
        firstNode = firstNode->next;
    }
    else {
        chainNode<T>* p = firstNode;
        //找到前驱节点
        for (int i = 0; i < theIndex - 1; i++) {
            p = p->next;
        }
        deleteNode = p->next;
        p->next = p->next->next;
    }
    listSize--;
    delete deleteNode;
}
//逆置
template<class T>
void chain<T>::reverse() {
    if (firstNode == NULL || firstNode->next == NULL) return;
    chainNode<T>* pastNode = firstNode;
    chainNode<T>* currentNode = pastNode->next;
    chainNode<T>* nextNode = currentNode->next;
    firstNode->next = NULL;
    while (nextNode != NULL) {
        currentNode->next = pastNode;
        pastNode = currentNode;
        currentNode = nextNode;
        nextNode = nextNode->next;
    }
    currentNode->next = pastNode;
    firstNode = currentNode;
}
//输出
template<class T>
int chain<T>::Output() {
    int count = 0, index = 0;
    iterator beginning = begin();
    while (index < listSize) {
        count += index ^ (*beginning);
        beginning++;
        index++;
    }
    return count;
}
//冒泡排序
template<class T>
void chain<T>::bubbleSort() {
    chainNode<T>* p1, * p2;
    for (int i = 0; i < listSize - 1; i++) {
        p1 = firstNode;
        p2 = p1->next;
        T x;
        for (int j = 0; j < listSize - 1; j++) {
            if (p1->element > p2->element) {
                x = p1->element;
                p1->element = p2->element;
                p2->element = x;
            }
            p1 = p2;
            p2 = p2->next;
        }
    }
}
//合并
template<class T>
void chain<T>::merge(chain<T>& a, chain<T>& b) {
    //两个迭代器,用于遍历两个链表
    chain<T>::iterator i(a.firstNode);
    chain<T>::iterator j(b.firstNode);
    int x = 0;//计数器,记录合并后链表中的位置
    //当两个链表都没合并完时
    while (i != NULL && j != NULL) {
        if (*i > *j) {//比较当前元素大小
            insert(x, *j);//插入小的
            j++;
            x++;
        }
        else {
            insert(x, *i);
            i++; 
            x++;
        }
    }
    //至少一个链表已经遍历完成,直接插入到链表末尾
    while (i != NULL) {
        insert(x, *i);
        i++;
        x++;
    }
    while (j != NULL) {
        insert(x, *j);
        j++;
        x++;
    }
}
int main() {
    int N, M;
    cin >> N >> M;
    chain<int>chain1(N);
    chain<int>chain2(M);
    chain<int>chain3(M+N);
    int result[3];
    for (int i = 0; i < N; i++) {
        int c = 0;
        cin >> c;
        chain1.insert(i, c);
    }
    for (int j = 0; j < M; j++) {
        int c = 0;
        cin >> c;
        chain2.insert(j, c);
    }
    chain1.bubbleSort();
    chain2.bubbleSort();
    result[0] = chain1.Output();
    result[1] = chain2.Output();

    chain3.merge(chain1, chain2);
    result[2] = chain3.Output();
    for (int i = 0; i < 3; i++) {
        cout << result[i] << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值