【C++习题集】-- 顺序表、链表

news2025/1/25 5:03:11

(用于复习)

目录

线性表

顺序表

链表

单链表

单向 \ 双向

带哨兵位 \ 不带哨兵位

循环 \ 非循环

无头单向非循环链表实现

oj题

203. 移除链表元素

206. 反转链表

快慢指针

141.环形链表

【解题思路】

带头双向循环链表

顺序表和链表的区别


线性表

        常见的线性表:顺序表链表队列字符串...。

  • 逻辑上: 线性结构
  • 物理上: 不一定是连续

顺序表

#include <iostream>
#include <cassert>
#include <malloc.h>
#include <cstdlib>

namespace qcr_vector
{
    typedef int VectorType;

    struct Vector
    {
        VectorType* _array;  // 指向动态开辟的数组
        uint64_t _size;        // 有效数据个数
        uint64_t _capacity;    // 容量空间的大小
    };
    /*****************
     * 顺序表初始化
     *****************/
    void VectorInit(Vector* vector)
    {
        assert(vector);

        vector->_array = nullptr;
        vector->_capacity = vector->_size = 0;
    }

    /*****************
     * 检查空间,如果满了,进行增容
     *****************/
    void VectorCapacity(Vector* vector)
    {
        assert(vector);

        if(vector->_capacity == vector->_size)
        {
            uint64_t new_capacity = vector->_capacity == 0 ? 5 : vector->_capacity * 2;
            VectorType* tmp = (VectorType*)realloc(vector->_array, new_capacity * sizeof(VectorType));
            if(tmp == nullptr)
            {
                perror("VectorCapacity::realloc");
                exit(-1);
            }
            vector->_array = tmp;
            vector->_capacity = new_capacity;
        }
    }

    // 顺序表在pos位置插入element
    void VectorInsert(Vector *vector, uint64_t pos, VectorType element)
    {
        assert(vector);
        assert(pos < vector->_size);

        VectorCapacity(vector);
        for(int i = vector->_size; i > pos; i--)
        {
            vector->_array[i] = vector->_array[i - 1];
        }
        vector->_array[pos] = element;
        (vector->_size)++;
    }

    // 顺序表删除pos位置的值
    void VectorErase(Vector *vector, uint64_t pos)
    {
        assert(vector);
        assert(pos < vector->_size);

        for(int i = pos; i < vector->_size - 1; i--)
        {
            vector->_array[i] = vector->_array[i + 1];
        }
        (vector->_size)--;
    }

    // 顺序表尾插
    void VectorPushBack(Vector* vector, VectorType element)
    {
        VectorInsert(vector, vector->_size, element);
        // assert(vector);

        // VectorCapacity(vector);
        // vector->_array[vector->_size] = element;
        // (vector->_size)++;
    }

    // 顺序表尾删
    void VectorPopBack(Vector* vector)
    {
        VectorErase(vector, vector->_size - 1);
        // assert(vector);

        // (vector->_size)--;
    }

    // 顺序表头插
    void VectorPushFront(Vector* vector, VectorType element)
    {
        VectorInsert(vector, 0, element);
        // assert(vector);

        // VectorCapacity(vector);
        // for(int i = vector->_size; i > 0; i--)
        // {
        //     vector->_array[i] = vector->_array[i - 1];
        // }
        // vector->_array[0] = element;
        // (vector->_size)++;
    }

    // 顺序表头删
    void VectorPopFront(Vector* vector)
    {
        VectorErase(vector, 0);
        // assert(vector);

        // for(int i = 0; i < vector->_size - 1; i--)
        // {
        //     vector->_array[i] = vector->_array[i + 1];
        // }
        // (vector->_size)--;
    }

    // 顺序表查找
    int64_t VectorFind(Vector *vector, VectorType element)
    {
        assert(vector);

        for(int i = 0; i < vector->_size; i++)
        {
            if(vector->_array[i] == element)
            {
                return i;
            }
        }
        return -1;
    }

    // 顺序表销毁
    void VectorDestory(Vector *vector)
    {
        assert(vector);
        
        vector->_size = vector->_capacity = 0;
        if(vector->_array)
            free(vector->_array);
        vector->_array = nullptr;
    }

    // 顺序表打印
    void VectorPrint(Vector *vector)
    {
        assert(vector);

        for(uint64_t i = 0; i < vector->_size; i++)
        {
            std::cout << vector->_array[i] << " ";
        }
        std::cout << std::endl;
    }
};

链表

单链表

单向 \ 双向

带哨兵位 \ 不带哨兵位

循环 \ 非循环

最常用还是两种结构:

  • 无头单向非循环链表
  • 带头双向循环链表

  1. 无头单向非循环链表:一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。笔试面试出现很多)
  2. 带头双向循环链表:一般用在单独存储数据。

无头单向非循环链表实现

#include <iostream>
#include <cassert>

namespace qcr_single_list
{
    typedef int SingleListType;

    struct SListNode
    {
        SingleListType _data;
        SListNode *_next;
    };

    /****************
     * 动态申请一个结点
     ****************/
    SListNode *BuySListNode(SingleListType data)
    {
        SListNode *single_list = (SListNode *)malloc(sizeof(SListNode));
        if (single_list == nullptr)
        {
            perror("BuySListNode::malloc");
            exit(-1);
        }
        single_list->_data = data;
        single_list->_next = nullptr;
        return single_list;
    }

    /****************
     * 单链表在pos位置之后插入data
     ****************/
    void SListInsertAfter(SListNode *pos, SingleListType data)
    {
        assert(pos);

        SListNode *single_list = BuySListNode(data);
        single_list->_next = pos->_next;
        pos->_next = single_list;
    }

    /****************
     * 单链表删除pos位置之后的值
     ****************/
    void SListEraseAfter(SListNode *pos)
    {
        assert(pos);
        assert(pos->_next);
        SListNode *erase = pos->_next;
        pos->_next = pos->_next->_next;
        free(erase);
    }

    /****************
     * 单链表头插
     ****************/
    void SListPushFront(SListNode **single_list, SingleListType data)
    {
        SListNode *new_SListNode = BuySListNode(data);
        new_SListNode->_next = (*single_list);
        *single_list = new_SListNode;
    }

    /****************
     * 单链表尾插
     ****************/
    void SListPushBack(SListNode **single_list, SingleListType data)
    {
        SListNode *new_SListNode = BuySListNode(data);
        if (*single_list) // 尾插
        {
            SListNode *cur = *single_list;
            while (cur->_next)
            {
                cur = cur->_next;
            }
            cur->_next = new_SListNode;
        }
        else // 头插
        {
            *single_list = new_SListNode;
        }
    }

    /****************
     * 单链表头删
     ****************/
    void SListPopFront(SListNode **single_list)
    {
        assert(*single_list);

        SListNode *erase = *single_list;
        *single_list = (*single_list)->_next;
        free(erase);
    }

    /****************
     * 单链表尾删
     ****************/
    void SListPopBack(SListNode **single_list)
    {
        assert(*single_list);

        if (nullptr == (*single_list)->_next)
        {
            free(*single_list);
            *single_list = nullptr;
        }
        else
        {
            SListNode* cur = *single_list;
            while (cur->_next->_next)
            {
                cur = cur->_next;
            }

            SListNode *erase = cur->_next;
            cur->_next = nullptr;
            free(erase);
        }
    }

    /****************
     * 单链表查找
     ****************/
    SListNode *SListFind(SListNode *single_list, SingleListType data)
    {
        assert(single_list);
        SListNode *cur = single_list;
        while (cur)
        {
            if (cur->_data == data)
                return cur;
            cur = cur->_next;
        }
        return nullptr;
    }

    /****************
     * 单链表打印
     ****************/
    void SListPrint(SListNode *single_list)
    {
        SListNode *cur = single_list;
        while (cur != nullptr)
        {
            printf("%d->", cur->_data);
            cur = cur->_next;
        }
        printf("nullptr\n");
    }

    /****************
     * 单链表销毁
     ****************/
    void SListDestory(SListNode** single_list)
    {
        assert(*single_list);
	    SListNode* cur = *single_list;
	    while (cur)
	    {
		    SListNode* next = cur->_next;
		    free(cur);
		    cur = _next;
	    }
	    *single_list = nullptr;
    }
}

oj题

203. 移除链表元素

203. 移除链表元素 - 力扣(LeetCode)


/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* cur = head;
        ListNode* prev = nullptr;
        while(cur)
        {
            if(cur->val == val)
            {
                if(prev == nullptr)
                {
                    head = head->next;
                    cur = head;
                }
                else
                {
                    prev->next = cur->next;
                    cur = cur->next;
                }
            }
            else
            {
                prev = cur;
                cur = cur->next;
            }
        }
        return head;
    }
};

206. 反转链表

206. 反转链表 - 力扣(LeetCode)


/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head)
    {
        ListNode* ret = nullptr;
        ListNode* cur = head;
        while(cur)
        {
            ListNode* next = cur->next;

            cur->next = ret;
            ret = cur;

            cur = next;
        }
        return ret;
    }
};

链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)
 


        双指针实现。

易错:
        两个临界需要全部关注到。

  • 1,{1,2,3,4,5}
  • 5,{1,2,3,4,5}
/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
#include <asm-generic/errno.h>
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
		ListNode* cur = pListHead;
		while(k--)
		{
			if(cur)
				cur = cur->next;
			else
			 	return nullptr;
		}

		ListNode* prev = pListHead;

		while(cur)
		{
			cur = cur->next;
			prev = prev->next;
		}
		return prev;
    }
};

(链表OJ题一定要确定边界,防止使用nullptr指针)

快慢指针

141.环形链表

141. 环形链表 - 力扣(LeetCode)


【解题思路】
        让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环
运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。

带头双向循环链表

#include <iostream>
#include <malloc.h>
#include <cassert>

namespace qcr_list
{
    typedef int ListDataList;
    struct ListNode
    {
        ListDataList _data;
        ListNode* _next;
        ListNode* _prev;
    };

    // 带头+双向+循环链表增删查改实现
    ListNode* Init()
    {
        ListNode* head_node = (ListNode*)malloc(sizeof(ListNode));
        head_node->_prev = head_node;
        head_node->_next = head_node;
        return head_node;
    } 

    // 创建新结点
    ListNode* ListCreate(ListDataList data)
    {
        ListNode* new_node = (ListNode*)malloc(sizeof(ListNode));
        if(nullptr == new_node)
            perror("ListCreate::malloc");
        new_node->_data = data;
        new_node->_prev = new_node->_next = nullptr;
        return new_node;
    }

    // 双向链表尾插
    void ListPushBack(ListNode* pHead, ListDataList data)
    {
        assert(pHead);

        ListNode* tail = pHead->_prev;
        ListNode* new_node = ListCreate(data);
        tail->_next = new_node;
        new_node->_next = pHead;

        new_node->_prev = tail;
        pHead->_prev = new_node;
    }   

    // 双向链表头插
    void ListPushFront(ListNode* pHead, ListDataList data)
    {
        assert(pHead);

        ListNode* next = pHead->_next;
        ListNode* new_node = ListCreate(data);
        pHead->_next = new_node;
        new_node->_next = next;

        new_node->_prev = pHead;
        next->_prev = new_node;
    }

    // 双向链表尾删
    void ListPopBack(ListNode* pHead)
    {
        assert(pHead);

        if(pHead->_next == pHead)
            return;

        ListNode* erase = pHead->_prev;
        erase->_prev->_next = pHead;
        pHead->_prev = erase->_prev;

        erase->_next = erase->_prev = nullptr;
        free(erase);
        erase = nullptr;
    }

    // 双向链表头删
    void ListPopFront(ListNode* pHead)
    {
        assert(pHead);

        if(pHead->_next == pHead)
            return;

        ListNode* erase = pHead->_next;
        erase->_next->_prev = pHead;
        pHead->_next = erase->_next;

        erase->_next = erase->_prev = nullptr;
        free(erase);
        erase = nullptr;
    }

    // 双向链表插入
    void ListInsert(ListNode* pos, ListDataList data)
    {
        assert(pos);

        ListNode* new_node = ListCreate(data);
        ListNode* prev = pos->_prev;
        prev->_next = new_node;
        new_node->_next = pos;

        new_node->_prev = prev;
        pos->_prev = new_node;
    }

    // 双向链表删除
    void ListErase(ListNode* pos)
    {
        assert(pos);
        ListNode* prev = pos->_prev;
        ListNode* next = pos->_next;

        pos->_next = pos->_prev = nullptr;
        free(pos);
        pos = nullptr;

        prev->_next = next;
        next->_prev = prev;
    }

    // 双向链表打印
    void ListPrint(ListNode* pHead)
    {
        assert(pHead);

        ListNode* cur = pHead->_next;
        while(pHead != cur)
        {
            std::cout << cur->_data << "->";
            cur = cur->_next;
        }
        std::cout << "nullptr" << std::endl;
    }

    // 双向链表查找
    ListNode* ListFind(ListNode* pHead, ListDataList data)
    {
        assert(pHead);
        
        ListNode* cur = pHead->_next;
        while(pHead != cur)
        {
            if(data == cur->_data)
            {
                return cur;
            }
            cur = cur->_next;
        }
        return nullptr;
    }


    // 双向链表销毁
    void ListPrint(ListNode* pHead)
    {
        assert(pHead);
        
        ListNode* cur = pHead;
        while(nullptr != cur)
        {
            if(cur == pHead)
            {
                cur->_prev->_next = nullptr;
            }
            ListNode* next = cur->_next;
            cur->_next = cur->_prev = nullptr;
            free(cur);
            cur = next;
        }
    }
}

顺序表和链表的区别

顺序表和链表的区别
不同点
顺序表
链表
存储空间上
物理上一定连续
逻辑上连续,但物理上不一定连续
随机访问
支持O(1)
不支持:O(N)
任意位置插入 \ 删除元素
可能需要搬移元素,效率低O(N)
只需修改指针指向
插入
动态顺序表,空间不够时需要扩容
没有容量的概念
应用场景
元素高效存储+频繁访问
任意位置插入和删除频繁
缓存利用率

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/965405.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows系统开机自启打开指定网页

windows系统开机自启打开指定网页 1、在电脑桌面右击新建快捷方式&#xff0c;输入想要开机打开的网址 2、点击下一步输入自己想要命名的名字 3、使用快捷键winR运行&#xff0c;输入shell:startup&#xff0c;点击确定 4、把在桌面创建快捷方式拉到启动文件夹里面 5、这样就完…

冠达管理:“旺季”来临,煤炭板块走高,云煤能源、陕西黑猫涨停

煤炭板块1日盘中发力走高&#xff0c;截至发稿&#xff0c;云煤动力、陕西黑猫涨停&#xff0c;兖矿动力涨超7%&#xff0c;晋控煤业、华阳股份涨超6%&#xff0c;山西焦煤、平煤股份涨超5%。 组织表明&#xff0c;动力大通胀背景下&#xff0c;未来3-5年煤炭供需偏紧的格局仍…

了解JVM(JavaEE初阶系列19)

目录 前言&#xff1a; 1.JVM是如何运行的 2.JVM中的内存区域划分 3.JVM的类加载机制 3.1JVM加载机制的五大步骤 3.1.1加载 3.1.1验证 3.1.1准备 3.1.1解析 3.1.1初始化 3.2总结 3.3JVM启动时机 3.4双亲委派模型 4.JVM中的垃圾回收策略 4.1JVM垃圾回收机制概念 …

ssh常用操作

ssh常用操作 SSH是一种安全协议&#xff0c;ssh是该协议的客户端程序&#xff0c;openssh-server则是该协议的服务端程序 常用系统都自带了ssh客户端程序&#xff0c;服务端程序则可能要安装 密码远程登陆 前提&#xff1a;服务器安装了openssh-server&#xff0c;未安装时…

9.1QTday3作业

1 getSaveFileName //保存文件按钮对应的槽函数 void Widget::on_save_btn_clicked() {//调用QFileDialog的静态成员函数getSaveFileName来获取选中的文件路径QString fileName QFileDialog::getSaveFileName(this,"保存文件","./","Image File(*.p…

SQL知识点合集(最新)

SQL执行顺序 left join on and 和 inner join on and的多条件查询区别 left join on后面的and条件判断字段必须是左表 inner join on后面的and条件判断字段可以是左表或者右表 -- 查询一个课程包含那些题 SELECT c.id,t.title,t.id from course c left JOIN topical t ON t.cou…

Qt +VTK+Cmake 编译和环境配置(第三篇,高级篇, 已解决)

上篇说了&#xff0c;Cmake 虽然可以成功的build&#xff0c;但是大部分人都选择的是VS编译&#xff0c;没有人选择Qt自带的编译器编译。 在build文件夹 shift右键 进入cmd串口&#xff0c;执行mingw32-make mingw32-make 报错&#xff01;&#xff01;&#xff01;&#x…

Springboot整合logback多节点日志文件加端口号区分

描述&#xff1a;正常情况下&#xff1a;在Springboot 项目的resources目录下创建一个logback-spring.xml的日志配置文件&#xff0c;简单配置如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?><configuration debug"false"&g…

jQuery成功之路——jQuery的DOM操作简单易懂

jQuery的DOM操作 1.jQuery操作内容 jQuery操作内容 1. text() 获取或修改文本内容 类似于 dom.innerText 2. html() 获取或修改html内容 类似 dom.innerHTML 注意: 1. text() 是获取设置所有 2. html() 是获取第一个,设置所有 <!DOCTYPE html> <html lang"zh…

Linux字符设备中的两个重要结构体(file、inode)

https://www.cnblogs.com/chen-farsight/p/6177870.html

PPT分享:EasyDarwin行业赋能 | 一种效率提升10倍的音视频开发方法

EasyAVFilter本质上就是将ffmpeg.exe改造成了动态库, 方便应用程序集成ffmpeg的各项功能&#xff1b; 详细信息可以直接看https://www.easydarwin.org/tools/153.html&#xff0c;具体用法和场景&#xff0c;看视频介绍&#xff1b;

OpenCV(十四):ROI区域截取

在OpenCV中&#xff0c;你可以使用Rect对象或cv::Range来截取图像的感兴趣区域&#xff08;Region of Interest&#xff0c;ROI&#xff09;。 方法一&#xff1a;使用Rect对象截取图像 Rect_(_Tp _x&#xff0c; _Tp _y&#xff0c; _Tp _width,_Tp _height) Tp:数据类型&…

Postgresql数据库操作

修改Jsonb中某个键值 例如 如果为 1 则修改成男 0则为女 UPDATE "large_screen"."overall_statistics" SET statistics_data CASE WHEN statistics_data->>fault_type 故障 THEN jsonb_set(statistics_data, {fault_type}, "fault")WH…

【Locomotor运动模块】抓取:按朝向抓取(Orientation Handler)案例

文章目录 案例原理 案例 左右手柄抓宝剑时&#xff0c;宝剑的朝向不同 L35 一个手柄对应一个抓取点 原理 1、左右手柄分别抓取的是宝剑上的不同抓取点——GenericOrientation Handle通用朝向把手 它是我们设置“按朝向抓取”&#xff08;Orientation Handler&#xff09;时&…

漂浮岛场景WebGL效果解析

访问在线地址&#xff0c;代码在此处。 场景构图 该场景使用了3个岩石模型&#xff0c;一些通用的阙类植物、树木模型&#xff0c;还有空中的鸟类模型。 场景的渲染顺序&#xff1a;深度预通道&#xff0c;岩石&#xff0c;鸟类&#xff0c;天空&#xff0c;云粒子。 相机…

机器人中的数值优化(六)—— 线搜索最速下降法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

Python|小游戏之猫捉老鼠!!!

最近闲(mang)来(dao)无(fei)事(qi)&#xff0c;喜欢研究一些小游戏&#xff0c;本篇文章我主要介绍使用 turtle 写的一个很简单的猫捉老鼠的小游戏&#xff0c;主要是通过鼠标控制老鼠(Tom)的移动&#xff0c;躲避通过电脑控制的猫(Jerry)的追捕。 游戏主体思考逻辑&#xff1…

良品铺子聚焦高品质,打造零食王国

出品| 大力财经 文 | 魏力 8月29日&#xff0c;休闲食品企业良品铺子发布2023年半年报显示&#xff0c;实现营业收入39.87亿元&#xff0c;归母净利润1.89亿元。 值得注意的是&#xff0c;在全球经济增速不强的背景下&#xff0c;良品铺子上半年&#xff0c;新开店323家&#…

孙哥Spring源码第15集

第15集 BeanPostProcessor、BeanFactroyPostProcessor 【视频来源于&#xff1a;B站up主孙帅suns Spring源码视频】 1、Spring预先给我们提供的PostProcessor的有哪些 是怎么来的&#xff1f; 2、Spirng中非常重要的扩展点 BeanPostProcessor 和BeanFactoryPostProcesor Aut…

IA-SEG项目中DIAL-Filters(IAPM模块+LGF模块)使用解读

IA-SEG项目源自论文Improving Nighttime Driving-Scene Segmentation via Dual Image-adaptive Learnable Filters&#xff0c;其核心就是在原有的语义分割模型上添加了DIAL-Filters。而&#xff0c;DIAL-Filters由两部分组成&#xff0c;包括一个图像自适应处理模块&#xff0…