C++——list的使用及其模拟实现

news2024/11/16 19:32:43

list

文章目录

  • list
  • 1. 基本使用
    • 1.1 list对象的定义
    • 1.2 增(插入数据)
    • 1.3 删(删除数据)
    • 1.4 遍历访问
  • 2. 模拟实现
    • 2.1 节点类ListNode
    • 2.2 封装ListNode类,实现list基本功能
    • 2.3 实现迭代器iterator
      • 2.3.1 实现const迭代器const_iterator
      • 2.3.2 实现反向迭代器reverse_iterator
  • 3. list容器模拟实现代码

本章思维导图
在这里插入图片描述注:本章思维导图对应的 .png.xmind文件都以同步至 资源,可免费查阅


list也是C++标准模板库中的一大容器,事实上,他就是我们在数据结构中学的带头循环双向链表

在这里插入图片描述

接下来,让我们来学习它的基本使用以及模拟实现:

1. 基本使用

1.1 list对象的定义

template < class T, class Alloc = allocator<T> > class list;
  • 作为标准模板库中的容器,list同样也是一个类模板
  • 如果要定义一个list对象,就需要指定其存储的数据类型T。例如:list<int>

1.2 增(插入数据)

list作为带头循环双向链表,其支持头插(push_front)、尾插(push_back)、在pos位置之前插入(insert)等添加数据的操作

void push_back (const value_type& val);

void push_front (const value_type& val);

iterator insert (iterator position, const value_type& val);

接下来做简单的演示:

#include <iostream>
#include <list>

using namespace std;

int main()
{
	list<int> lt;

	//尾插
	for (int i = 0; i < 3; i++)	lt.push_back(i);
	for (auto& e : lt)	cout << e << " ";
	cout << endl;

	//头插
	for (int i = 3; i < 6; i++)	lt.push_front(i);
	for (auto& e : lt)	cout << e << " ";
	cout << endl;

	//在`pos`前插入
	lt.insert(lt.end(), 99);
	for (auto& e : lt)	cout << e << " ";
	cout << endl;

	return 0;
}

output:

0 1 2
5 4 3 0 1 2
5 4 3 0 1 2 99

1.3 删(删除数据)

和添加数据一样,list删除数据同样也支持**头删(pop_front)、尾删(pop_back)、删除pos位置的节点(erase)**等删除操作

void pop_front();

void pop_back();

iterator erase (iterator position);

接下来做简单的演示:

#include <iostream>
#include <list>

using namespace std;

int main()
{
	list<int> lt;
	for (int i = 0; i < 5; i++) lt.push_back(i + 1);

	//尾删
	lt.pop_back();
	for (auto& e : lt)	cout << e << " ";
	cout << endl;

	//头删
	lt.pop_front();
	for (auto& e : lt)	cout << e << " ";
	cout << endl;

	//删除`pos`处的节点
	lt.erase(lt.begin());
	for (auto& e : lt)	cout << e << " ";
	cout << endl;

	return 0;
}

output:

1 2 3 4
2 3 4
3 4

1.4 遍历访问

不同于stringvectorlist并不支持[]访问。list只有两种访问方式:

  • 迭代器访问
  • 范围for
  • 实际上,范围for使用的同样也是迭代器
  • 关于list的迭代器后面会做详细说明,这里仅做使用展示

接下来做简单的演示:

#include <iostream>
#include <list>

using namespace std;

int main()
{
	list<int> lt;
	for (int i = 0; i < 5; i++)	lt.push_back(i + 1);
	
    //迭代器访问
	list<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	//范围for
	for (auto& e : lt)	cout << e << " ";
	cout << endl;

	return 0;
}

2. 模拟实现

不同于stringvector这种连续的结构,对于存储空间不连续的list而言,其模拟实现就要复杂了许多

接下来进行逐步分析:

2.1 节点类ListNode

  • list是带头循环双向链表,其每个节点都存储着:前一个节点和后一个节点的指针以及存储的数据。
  • 因此,我们需要创建一个ListNode类来存储对应的信息
template<class T>
struct ListNode
{
	ListNode<T>* _next;
	ListNode<T>* _prev;
	T	         _data;
	
    //构造函数
	ListNode(const T& value = T())
		: _next(nullptr)
		, _prev(nullptr)
		, _data(value)
	{}
};

注:

  • 不要忘记在C++中**struct不是结构体而是一个类**,只是其默认的访问限定符为public。因此它也需要构造函数
  • ListNOde是一个类模板,所以ListNode不是类名,而是模板名;ListNode<T>才是类名

2.2 封装ListNode类,实现list基本功能

实现好了ListNOde类后,我们就可以将其封装进list类中,利用list对其进行管理:

template<class T>
class list
{
    typedef ListNode<T> Node;

public:
    //构造
    list()
    {
        _head = new Node;
        _head->_next = _head;
        _head->_prev = _head;
    }
	
    //尾插
    void push_back(const T& value = T())
    {
        Node* newNode = new Node(value);

        Node* tail = _head->_prev;
        tail->_next = newNode;
        newNode->_prev = tail;
        newNode->_next = _head;
        _head->_prev = newNode;
    }
	
    //尾删
    void pop_back()
    {
        assert(begin() != end());

        Node* tail = _head->_prev;
        Node* newTail = tail->_prev;

        _head->_prev = newTail;
        newTail->_next = _head;

        delete tail;
    }
	
    //头插
    void push_front(const T& value = T())
    {
        Node* cur = _head->_next;
        Node* newNode = new Node(value);

        _head->_next = newNode;
        newNode->_prev = _head;
        newNode->_next = cur;
        cur->_prev = newNode;
    }
	
    //头删
    void pop_front()
    {
        assert(begin() != end());

        Node* cur = _head->_next;

        cur->_next->_prev = _head;
        _head->_next = cur->_next;

        delete cur;
    }
	
    //交换两个list
    void swap(list<T>& it)
    {
        std::swap(_head, it._head);
    }
private:
    Node* _head;
};

注:部分需要用到迭代器的功能放到后面实现

2.3 实现迭代器iterator

让我们再来回顾一下C++迭代器的概念:

迭代器是一个设计模式,它允许你遍历一个容器(如数组、列表、向量等)的所有元素,而无需知道容器的底层表示方式

也就是说,迭代器为我们提供了一个统一的方式来遍历容器。例如我们++一个迭代器就可以指向其后一个元素,--一个迭代器就可以指向前一个元素,而不要管这个容器具体是什么。

  • 迭代器的底层实际上都是指针
  • 对于stringvector这种连续的物理空间,他们的迭代器很好实现,就是原生指针
  • 但是对于list这种物理空间并不连续的存储结构一个节点的指针++后并不能指向它后面的指针,因此就不能使用原生指针来实现其迭代器了。

因此,为了让屏蔽 节点的指针++后不能指向它后面的指针 这一缺陷,我们就需要对ListNode类进行封装使其++- - 等操作符合迭代器的规范。而对其封装的类,就是我们的迭代器__List_iterator

template<class T>
struct __list_iterator
{
    typedef ListNode<T> Node;
    typedef __list_iterator<T> self;

    Node* _node;

    __list_iterator(Node* node)
        : _node(node)
        {}

    //++it
    self& operator++ ()
    {
        _node = _node->_next;
        return *this;
    }
    //it++
    self operator++ (int)
    {
        self temp(_node);
        _node = _node->_next;
        return temp;
    }
    //--it
    self& operator-- ()
    {
        _node = _node->_prev;
        return *this;
    }
    //it--
    self operator-- (int)
    {
        self temp(_node);
        _node = _node->_prev;
        return temp;
    }
    T& operator* ()
    {
        return _node->_data;
    }
    T* operator-> ()
    {
        return &_node->_data;
    }
    bool operator!= (const self& value)
    {
        return _node != value._node;
    }
    bool operator == (const self& value)
    {
        return _node == value._node;
    }
};

2.3.1 实现const迭代器const_iterator

要实现一个const迭代器,最容易想到的方法,当然就是复制一份普通的迭代器__list_iterator对象,再对operator*()operator->()的返回值做简单的修改就行了:

template<class T>
struct __list_const_iterator
{
    typedef ListNode<T> Node;
    typedef __list_const_iterator<T> self;

    Node* _node;

    __list_const_iterator(Node* node)
        : _node(node)
        {}

    //++it
    self& operator++ ()
    {
        _node = _node->_next;
        return *this;
    }
    //it++
    self operator++ (int)
    {
        self temp(_node);
        _node = _node->_next;
        return temp;
    }
    //--it
    self& operator-- ()
    {
        _node = _node->_prev;
        return *this;
    }
    //it--
    self operator-- (int)
    {
        self temp(_node);
        _node = _node->_prev;
        return temp;
    }
    const T& operator* ()
    {
        return _node->_data;
    }
    const T* operator-> ()
    {
        return &_node->_data;
    }
    bool operator!= (const self& value)
    {
        return _node != value._node;
    }
    bool operator == (const self& value)
    {
        return _node == value._node;
    }
};

但是,这样的做法无疑会添加大量冗余的代码,显然不是最恰当的。因此最初编写标准模板库的大佬们就想出了这样的办法——添加类模板参数。也就是说,我们可以将__list_iterator类模板写成这样:

template<class T, class Referance, class Ptr>
struct __list_iterator
{}
  • T就是list存储的数据类型
  • Referance就是operator*()的返回值,如果是非const对象,就传入T&;如果是const对象,就传入const T&
  • Ptr就是operator->()的返回值,如果是非const对象,就传入T*;如果是const对象,就传入const T*

这样,我们就可以将普通迭代器和const迭代器整合为一个迭代器__list_iterator

template<class T, class Referance, class Ptr>
struct __list_iterator
{
    typedef ListNode<T> Node;
    typedef __list_iterator<T, Referance, Ptr> self;

    Node* _node;

    __list_iterator(Node* node)
        : _node(node)
        {}

    //++it
    self& operator++ ()
    {
        _node = _node->_next;
        return *this;
    }
    //it++
    self operator++ (int)
    {
        self temp(_node);
        _node = _node->_next;
        return temp;
    }
    //--it
    self& operator-- ()
    {
        _node = _node->_prev;
        return *this;
    }
    //it--
    self operator-- (int)
    {
        self temp(_node);
        _node = _node->_prev;
        return temp;
    }
    Referance operator* ()
    {
        return _node->_data;
    }
    Ptr operator-> ()
    {
        return &_node->_data;
    }
    bool operator!= (const self& value)
    {
        return _node != value._node;
    }
    bool operator == (const self& value)
    {
        return _node == value._node;
    }
};

2.3.2 实现反向迭代器reverse_iterator

需要注意:

为了实现对称性,C++规定:

  • 反向迭代器的开头rbegin()实际上是正向迭代器的结束end();反向迭代器的结束rend()即为正向迭代器的开始begin()
  • 当对一个反向迭代器进行*操作时,并不会对当前的位置进行*操作,而是会对当前位置的之前一个位置进行*操作

和const迭代器,一样写出反向迭代器最简单的方式同样是复制一下普通迭代器类,再修改一下代码逻辑即可。这里不再做展示。

但很显然,这种方法同样也不是最好的。

最初编写标准模板库的大佬们在写反向迭代器的代码时会思考这样的问题:

是否可以写这样一份代码,可以将所有的正向迭代器都转化为对应的反向迭代器呢?

事实上,他们确实是这样做的:

  • 可以新建一个头文件reverse_iterator,其包含一个反向迭代器类Reverse_iterator,其实现了关于反向迭代器的各种操作。
  • 为了做到可以 将所有的正向迭代器转化为其对应的反向迭代器 ,其各种操作并不需要我们手动实现,而是需要复用正向迭代器的方法

我们可以先来看看其具体的实现:

template<class Iterator, class Ref, class Ptr>
struct Reverse_iterator
{
	Reverse_iterator(const Iterator& it)
		: _it(it)
	{}

	typedef Reverse_iterator<Iterator, Ref, Ptr> Self;

	Self& operator++ ()
	{
		--_it;
		return *this;
	}

	Self& operator-- ()
	{
		++_it;
		return *this;
	}

	Self operator++ (int)
	{
		Self tmp = _it;
		--_it;

		return tmp;
	}

	Self operator-- (int)
	{
		Self tmp = _it;
		++_it;

		return tmp;
	}
	
	//注意,*运算不是对当前位置
	//而是对当前位置的前一个位置进行运算
	Ref operator* ()
	{
		Iterator tmp = --_it;
		++_it;

		return *tmp;
	}

	Ptr operator-> ()
	{
		return &(operator*());
	}

	bool operator!= (const Self& it)
	{
		return _it != it._it;
	}

	bool operator== (const Self& it)
	{
		return _it == it._it;
	}

	Iterator _it;
};
  • Iterator即为对应正向迭代器
  • 这里通过对Iterator的封装,利用Iterator的方法来间接实现其反向迭代器的操作,从而就可以实现用同一份代码就可以将所有的正向迭代器都转换为其对应的反向迭代器

3. list容器模拟实现代码

reverse_list.h

template<class Iterator, class Ref, class Ptr>
struct Reverse_iterator
{
	Reverse_iterator(const Iterator& it)
		: _it(it)
	{}

	typedef Reverse_iterator<Iterator, Ref, Ptr> Self;

	Self& operator++ ()
	{
		--_it;
		return *this;
	}

	Self& operator-- ()
	{
		++_it;
		return *this;
	}

	Self operator++ (int)
	{
		Self tmp = _it;
		--_it;

		return tmp;
	}

	Self operator-- (int)
	{
		Self tmp = _it;
		++_it;

		return tmp;
	}

	Ref operator* ()
	{
		Iterator tmp = --_it;
		++_it;

		return *tmp;
	}

	Ptr operator-> ()
	{
		return &(operator*());
	}

	bool operator!= (const Self& it)
	{
		return _it != it._it;
	}

	bool operator== (const Self& it)
	{
		return _it == it._it;
	}

	Iterator _it;
};

list.h

#pragma once

#include <iostream>
#include <list>
#include <assert.h>
#include "reverse_iterator.h"

using namespace std;

namespace TEST
{
	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T	         _data;

		ListNode(const T& value = T())
			: _next(nullptr)
			, _prev(nullptr)
			, _data(value)
		{}
	};
	
    //正向迭代器
	template<class T, class Referance, class Ptr>
	struct __list_iterator
	{
		typedef ListNode<T> Node;
		typedef __list_iterator<T, Referance, Ptr> self;

		Node* _node;

		__list_iterator(Node* node)
			: _node(node)
		{}

		//++it
		self& operator++ ()
		{
			_node = _node->_next;
			return *this;
		}
		//it++
		self operator++ (int)
		{
			self temp(_node);
			_node = _node->_next;
			return temp;
		}
		//--it
		self& operator-- ()
		{
			_node = _node->_prev;
			return *this;
		}
		//it--
		self operator-- (int)
		{
			self temp(_node);
			_node = _node->_prev;
			return temp;
		}
		Referance operator* ()
		{
			return _node->_data;
		}
		Ptr operator-> ()
		{
			return &_node->_data;
		}
		bool operator!= (const self& value)
		{
			return _node != value._node;
		}
		bool operator == (const self& value)
		{
			return _node == value._node;
		}
	};

	template<class T>
	class list
	{
		typedef ListNode<T> Node;

	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;
		typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;
		typedef Reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		list(list<T>& it)
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			for (const auto e : it)
			{
				push_back(e);
			}
		}

		list<T>& operator= (list<T> it)
		{
			swap(it);

			return *this;
		}

		void push_back(const T& value = T())
		{
			insert(end(), value);
		}

		void pop_back()
		{
			erase(--end());
		}

		void push_front(const T& value = T())
        {
			insert(begin(), value);
		}

		void pop_front()
		{
			erase(begin());
		}

		iterator erase(iterator pos)
		{
			assert(begin() != end());

			Node* curNode = pos._node;
			Node* prevNode = curNode->_prev;
			Node* nextNode = curNode->_next;

			nextNode->_prev = prevNode;
			prevNode->_next = nextNode;
			delete curNode;

			return nextNode;
		}

		iterator insert(iterator pos, const T& value = T())
		{
			Node* newNode = new Node(value);
			Node* prevNode = pos._node->_prev;
			Node* curNode = pos._node;

			//prevNode newNode curNode;
			prevNode->_next = newNode;
			newNode->_prev = prevNode;
			newNode->_next = curNode;
			curNode->_prev = newNode;

			return newNode;
		}

		void swap(list<T>& it)
		{
			std::swap(_head, it._head);
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

		iterator begin()
		{
			return _head->_next;
		}

		const_iterator begin() const
		{
			return _head->_next;
		}

		iterator end()
		{
			return _head;
		}

		const_iterator end() const
		{
			return _head;
		}

		reverse_iterator rbegin()
		{
			return end();
		}

		const_reverse_iterator rbegin() const
		{
			return end();
		}

		reverse_iterator rend() 
		{
			return begin();
		}
		
		const_reverse_iterator rend() const
		{
			return begin();
		}

		~list()
		{
			clear();

			delete _head;
			_head = nullptr;
		}

	private:
		Node* _head;
	};
}

本篇完
如果错误敬请指正

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

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

相关文章

使用Hutool工具包解析、生成XML文件

说明&#xff1a;当我们在工作中需要将数据转为XML文件、或者读取解析XML文件时&#xff0c;使用Hutool工具包中的XMLUtil相关方法是最容易上手的方法&#xff0c;本文介绍如何使用Hutool工具包来解析、生成XML文件。 开始之前&#xff0c;需要导入Hutool工具包的依赖 <de…

力扣hot100 柱状图中最大的矩形 单调栈

Problem: 84. 柱状图中最大的矩形 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考地址 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public static int largestRectangleArea(int[] height){Stack&l…

疯狂的方块

欢迎来到程序小院 疯狂的方块 玩法&#xff1a;两个以上相同颜色的方块连在一起&#xff0c;点击即可消除&#xff0c;不要让方块到达顶部&#xff0c;消除底部方块哦^^。开始游戏https://www.ormcc.com/play/gameStart/263 html <div id"gameDiv"> <canv…

fiber学习

React原理&#xff1a;通俗易懂的 Fiber - 掘金

nacos启动失败解决

报错信息 Caused by: com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (2,937 > 2,048). You can change this value on the server by setting the ‘max_allowed_packet’ variable. 情景复现 最近使用mac正在运行一个nacos的spri…

treeview

QML自定义一个TreeView&#xff0c;使用ListView递归 在 Qt5 的 QtQuick.Controls 2.x 中还没有 TreeView 这个控件&#xff08;在 Qt6 中出了一个继承自 TableView 的 TreeView&#xff09;&#xff0c;而且 QtQuick.Controls 1.x 中的也需要配合 C model 来自定义&#xff0c…

Win10 双网卡实现同时上内外网

因为需要同时上内网和外网&#xff0c;但公司做了网络隔离&#xff0c;不能同时上内外网&#xff0c;所以多加了块无线网卡&#xff0c;配置双网关实现同时上内外网&#xff0c;互不影响 打开 Windows PowerShell&#xff08;管理员&#xff09;&#xff0c;输入&#xff1a;ro…

Github 2024-01-30 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-01-30统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4TypeScript项目2Jupyter Notebook项目2HTML项目1Rust项目1C项目1 稳定扩散Web UI 创建周期&…

C++核心编程:类和对象 笔记

4.类和对象 C面向对象的三大特性为:封装,继承,多态C认为万事万物都皆为对象&#xff0c;对象上有其属性和行为 例如&#xff1a; 人可以作为对象&#xff0c;属性有姓名、年龄、身高、体重...,行为有走、跑、跳、说话...车可以作为对象&#xff0c;属性有轮胎、方向盘、车灯…

万兆网络数据传输-scp加速

在万兆甚至更高的网络带宽场景下 scp 的传输效率并不如人意。毕竟 scp 是旧时代的产物&#xff0c;那时千兆网络都很罕见。以下通过修改压缩方式的方法提升数据的传输速度。同时也采用 nc &#xff0c; bbcp 和 rsync 进行了对比测试。 目录 scp采用默认方式更改压缩算法为 aes…

seata 分布式

一、下载安装seata 已经下载好的朋友可以跳过这个步骤。这里下载的是seata1.6.1这个版本。 1、进入seata官网 地址&#xff1a; https://seata.io/zh-cn/index.html 2、进入下载 3、点击下载地址 下载地址&#xff1a; https://github.com/seata/seata 二、配置seata 进入c…

vue3项目中让echarts适应div的大小变化,跟随div的大小改变图表大小

目录如下 我的项目环境如下利用element-resize-detector插件监听元素大小变化element-resize-detector插件的用法完整代码如下&#xff1a;结果如下 在做项目的时候&#xff0c;经常会使用到echarts&#xff0c;特别是在做一些大屏项目的时候。有时候我们是需要根据div的大小改…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷9

某企业根据自身业务需求&#xff0c;实施数字化转型&#xff0c;规划和建设数字化平台&#xff0c;平台聚焦“DevOps开发运维一体化”和“数据驱动产品开发”&#xff0c;拟采用开源OpenStack搭建企业内部私有云平台&#xff0c;开源Kubernetes搭建云原生服务平台&#xff0c;选…

redis—Zset有序集合

目录 前言 1.常见命令 2.使用场景 3.渐进式遍历 4.数据库管理 前言 有序集合相对于字符串、列表、哈希、集合来说会有一-些陌生。它保留了集合不能有重复成员的 特点&#xff0c;但与集合不同的是&#xff0c;有序集合中的每个元素都有-个唯- -的浮 点类型的分数(score) …

c语言常量详解 全

c语言常量详解 全 一 常量的基本概念及分类二 常量的存储方式三 c语言常量和变量的具体区别四 字面常量详解4.1 常见类型的字面常量及其示例&#xff1a;4.2 字面常量的使用情况4.3 字面常量的优点 五 const 关键字常量详解5.1 const关键字在C语言中的使用情况&#xff1a;5.2 …

山海鲸可视化大屏:引领企业洞悉市场,提升客户价值的秘密武器

随着大数据时代的到来&#xff0c;企业面临着前所未有的机遇与挑战。如何从海量数据中挖掘出有价值的信息&#xff0c;洞察市场趋势&#xff0c;提升客户价值&#xff0c;成为了企业发展的重要课题。山海鲸可视化企业客户价值分析大屏&#xff0c;为企业提供了一个全新的解决方…

利用外卖系统源码构建高效的在线订餐平台

在当今数字化时代&#xff0c;外卖服务已成为人们日常生活中不可或缺的一部分。为了满足用户需求&#xff0c;许多创业者和企业都希望搭建自己的在线订餐平台。利用现有的外卖系统源码&#xff0c;可以快速构建一个高效、安全的在线订餐平台。本文将介绍如何利用外卖系统源码来…

播报 | 天空卫士入围FreeBuf《CCSIP 2023中国网络安全产业全景图》16个细分领域

2024年1月24&#xff0c;国内安全行业门户FreeBuf旗下FreeBuf咨询正式发布《CCSIP 2023中国网络安全产业全景图》&#xff08;第六版&#xff09;。 天空卫士成功入围SASE、数据防泄露&#xff08;DLP&#xff09;、分类分级、数据安全治理(解决方案)、数据安全管控&#xff08…

Django问题报错:Cannot resolve keyword ‘name‘ into field. Choices are: course, id

笔者在进行登录注册实验用户名已经注册过的操作时报错 一、错误位置 二、问题原因 使用Student模型时参数名错误 三、解决办法 修改为与Student模型中对应的参数名,问题解决

每日一题 力扣514自由之路

514. 自由之路 题目描述&#xff1a; 电子游戏“辐射4”中&#xff0c;任务 “通向自由” 要求玩家到达名为 “Freedom Trail Ring” 的金属表盘&#xff0c;并使用表盘拼写特定关键词才能开门。 给定一个字符串 ring &#xff0c;表示刻在外环上的编码&#xff1b;给定另一…