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

(用于复习)

目录

线性表

顺序表

链表

单链表

单向 \ 双向

带哨兵位 \ 不带哨兵位

循环 \ 非循环

无头单向非循环链表实现

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)
只需修改指针指向
插入
动态顺序表,空间不够时需要扩容
没有容量的概念
应用场景
元素高效存储+频繁访问
任意位置插入和删除频繁
缓存利用率

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis—常用数据结构

Redis—常用数据结构 &#x1f50e;数据结构与内部编码 Redis 中常用的数据结构包括 Strings—字符串Hashes—哈希表Lists—列表Sets—集合Sorted sets—有序集合 Redis 底层在实现上述数据结构时, 会在源码层面针对上述实现进行特定优化, 以达到节省时间 / 节省空间的效果 …

51单片机智能电风扇控制系统proteus仿真设计( 仿真+程序+原理图+报告+讲解视频)

51单片机智能电风扇控制系统仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 讲解视频1.主要功能&#xff1a;2.仿真3. 原理图4. 程序代码5.设计报告6. 设计资料内容清单 51单片机智能电风扇控制系统仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 仿真图…

Java学习笔记之----I/O(输入/输出)一

在变量、数组和对象中存储的数据是暂时存在的&#xff0c;程序结束后它们就会丢失。想要永久地存储程序创建的数据&#xff0c;就需要将其保存在磁盘文件中(就是保存在电脑的C盘或D盘中&#xff09;&#xff0c;而只有数据存储起来才可以在其他程序中使用它们。Java的I/O技术可…

pip切换源

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

搬家快递服务小程序的便利性

在当今快节奏的生活中&#xff0c;搬家可能是很多人都需要面对的问题。无论是新房子还是新办公室&#xff0c;都需要高效、便捷的搬家服务。本文将介绍如何使用第三方小程序制作平台&#xff0c;如乔拓云平台&#xff0c;开发一款高效便捷的搬家服务小程序。 1. 注册登录第三方…

DRM全解析 —— ADD_FB(2)

接前一篇文章&#xff1a;DRM全解析 —— ADD_FB&#xff08;1&#xff09; 本文参考以下博文&#xff1a; DRM驱动&#xff08;四&#xff09;之ADD_FB 特此致谢&#xff01; 上一回围绕libdrm与DRM在Linux内核中的接口&#xff1a; DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, d…

Vue框架--Vue中el和data的两种写法

data与el的2种写法 1.el有2种写法 (1).new Vue时候配置el属性。 (2).先创建Vue实例&#xff0c;随后再通过vm.$mount(#root)指定el的值。 2.data有2种写法 (1).对象式 (2).函数式 如何选择&#xff1a;目前哪种写法都可以&#xff0c;以后学习到组件时&#xff…

Web安全——穷举爆破下篇(仅供学习)

Web安全 一、常见的端口服务穷举1、hydra 密码穷举工具的使用2、使用 hydra 穷举 ssh 服务3、使用 hydra 穷举 ftp 服务4、使用 hydra 穷举 mysql 服务5、使用 hydra 穷举 smb 服务6、使用 hydra 穷举 http 服务7、使用 hydra 穷举 pop3 服务8、使用 hydra 穷举 rdp 服务9、使用…

学习振弦采集模块的开发基本原理

飞讯教学篇&#xff1a;学习振弦采集模块的开发基本原理 振弦采集模块是一种用于测量物体振动、形变、压力等物理量的电子设备。它通过测量物体的振动变化&#xff0c;可以得出物体在不同条件下的动态特性&#xff0c;对于工程设计、科学研究、医学检测等领域都有广泛应用。本…

Unity生命周期函数

1、Awake 当对象&#xff08;自己这个类对象&#xff0c;就是这个脚本&#xff09;被创建时 才会调用该生命周期函数 类似构造函数的存在 我们可以在一个类对象创建时进行一些初始化操作 2、OnEnable 失活激活&#xff08;这个勾&#xff09; 想要当一个对象&#xff08;游戏…

python web 开发与 Node.js + Express 创建web服务器入门

目录 1. Node.js Express 框架简介 2 Node.js Express 和 Python 创建web服务器的对比 3 使用 Node.js Express 创建web服务器示例 3.1 Node.js Express 下载安装 3.2 使用Node.js Express 创建 web服务器流程 1. Node.js Express 框架简介 Node.js Express 是一种…

ROS-5.自定义topic消息格式

自定义topic消息格式 1. 定义消息1.1. 定义msg文件1.2. 在package.xml中添加功能包依赖1.3. 在CMakeList.txt添加编译选项1.4. 编译 2.定义发布者和订阅者2.1 定义发布者2.2. 定义订阅者2.3. 修改CMakeList.txt2.4 编译 3. 使用消息3.1 启动ros主程序3.2. 启动发布者3.3 启动订…

Lesson3-5:OpenCV图像处理---模版匹配和霍夫变换

学习目标 掌握模板匹配的原理&#xff0c;能完成模板匹配的应用理解霍夫线变换的原理&#xff0c;了解霍夫圆检测知道使用OpenCV如何进行线和圆的检测 1 模板匹配 1.1 原理 所谓的模板匹配&#xff0c;就是在给定的图片中查找和模板最相似的区域&#xff0c;该算法的输入包括…

PQUEUE - Printer Queue

题目描述 The only printer in the computer science students union is experiencing an extremely heavy workload. Sometimes there are a hundred jobs in the printer queue and you may have to wait for hours to get a single page of output. Because some jobs are …

创建ffmpeg vs2019工程

0 写在前面 本文主要参考链接&#xff1a;https://www.cnblogs.com/suiyek/p/15669562.html 感谢作者的付出&#xff1b; 1 目录结构 2 下载yasm和nasm 如果自己在安装VS2019等IDE的时候已经安装了它们&#xff0c;则不用再单独进行安装&#xff0c;比如我这边已经安装了&a…

弹窗、抽屉、页面跳转区别 | web交互入门

当用户点击或触发浏览页面的某个操作&#xff0c;有很多web交互方式&#xff0c;可以大致分为弹窗、抽屉、跳转新页面三种web交互方式。虽然这三种web交互方式看起来没什么不同&#xff0c;但实际上弹窗、抽屉、跳转新页面对交互体验有蛮大的影响。 这需要UI\UX设计师针对不同…

【iOS】Masonry的基本使用

文章目录 前言一、使用Masonry的原因二、约束的常识三、Masonry的简单使用四、Masonry的用例总结 前言 暑假安装了cocoapods&#xff0c;简单使用其调用了SVGKit&#xff0c;但是没有学习Masonry&#xff0c;特此总结博客记录Masonry的学习 一、使用Masonry的原因 Masonry是一…

深入解析即时通讯App开发中的关键技术

即时通讯App开发在现代社交和通信领域中扮演着重要的角色。随着移动设备的普及和网络的高速发展&#xff0c;人们对即时通讯工具的需求不断增加。本篇文章将深入探讨即时通讯App开发中的关键技术&#xff0c;帮助读者了解该领域的最新动态和技术趋势。 基础架构和通信协议 现…

RabbitMQ工作模式-发布订阅模式

Publish/Subscribe&#xff08;发布订阅模式&#xff09; 官方文档&#xff1a; https://www.rabbitmq.com/tutorials/tutorial-three-python.html 使用fanout类型类型的交换器&#xff0c;routingKey忽略。每个消费者定义生成一个队列关绑定到同一个Exchange&#xff0c;每个…

某人事系统架构搭建设计记录

首发博客地址 https://blog.zysicyj.top/ 先大致列一下基础情况 架构必须是微服务 场景上涉及大量查询操作&#xff0c;分析操作 存在临时大量写入的场景 并发并不高 对高可用要求较高&#xff0c;不能挂掉 对安全要求高 要能过等保测试等三方测试 使用人数并不多&#xff0c;十…