list常用接口模拟实现

文章目录

    • 一、模拟list类的框架
    • 二、函数接口实现
      • 1、迭代器接口
      • 2、常用删除、插入接口
      • 3、常用其他的一些函数接口
      • 4、默认成员函数


一、模拟list类的框架

1、使用带哨兵的双向链表实现。
2、链表结点:

// List的结点类
template<class T>
struct ListNode
{ListNode<T>* _pPre; //后继指针ListNode<T>* _pNext; //前驱指针T _val; //数据//构造结点ListNode(const T& val = T()) :_val(val), _pPre(nullptr), _pNext(nullptr){}
};

3、list类的成员变量和构造双向链表的哨兵位结点函数。

 //让哨兵位结点指向自己
typedef ListNode<T> Node;
typedef Node* PNode;void CreateHead(){_pHead = new  Node;_pHead->_pNext = _pHead;_pHead->_pPre = _pHead;}PNode _pHead;   //哨兵位结点

二、函数接口实现

1、迭代器接口

list的迭代器是双向迭代器,支持++、–,但是它们在内存上储存是不连续的,无法简单通过指针去进行++、–操作,所以我们要对list的迭代器进行封装。
(1)list正向迭代器类
成员变量:两个结点指针。

typedef ListNode<T>* PNode;
PNode _pNode;	//结点指针
PNode _P;//保存哨兵位结点指针,用于判断解引用是否访问哨兵位结点

构造函数:

//构造函数 ,获取一个结点指针ListIterator(const PNode & pNode = nullptr, const PNode& const P = nullptr) :_pNode(pNode),_P(P){}

拷贝构造、赋值、析构函数:
因为_pNode的指针指向的内存是有list类释放的,所以该类无需进行资源清理,使用浅拷贝即可,所以拷贝、赋值、析构都使用编译器生成的即可。

重载操作符:

	//Ref为T& Ptr为T*typedef ListIterator<T, Ref, Ptr> Self;//解引用Ref operator*(){assert(_P != _pNode);return _pNode->_val;}//该运算符重载的意义为T为自定义类型时使用,迭代器可以通过该运算符直接访问自定义类型成员Ptr operator->(){return &(_pNode->_val);}//前置++Self& operator++(){_pNode = _pNode->_pNext;return *this;}//后置++Self operator++(int){Self tmp(_pNode);_pNode = _pNode->_pNext;return tmp;}//前置--Self& operator--(){_pNode = _pNode->_pPre;return *this;}//后置--Self& operator--(int){Self tmp(_pNode);_pNode = _pNode->_pPre;return tmp;}//比较bool operator!=(const Self& l){return l._pNode != _pNode;}bool operator==(const Self& l){return l._pNode == _pNode;}

获取成员变量函数:

 //获取该迭代器成员变量PNode get(){return _pNode;}

ListIterator类一览:

//Ref为T& Ptr为T*
template<class T, class Ref, class Ptr>
class ListIterator
{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;
public://构造函数 ,获取一个结点指针ListIterator(const PNode & pNode = nullptr, const PNode& const P = nullptr) :_pNode(pNode),_P(P){}Ref operator*(){assert(_P != _pNode);return _pNode->_val;}Ptr operator->(){return &(operator*());}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self tmp(_pNode);_pNode = _pNode->_pNext;return tmp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self tmp(_pNode);_pNode = _pNode->_pPre;return tmp;}bool operator!=(const Self& l){return l._pNode != _pNode;}bool operator==(const Self& l){return l._pNode == _pNode;}PNode get(){return _pNode;}
private:PNode _pNode;PNode _P;
};
};

(2)反向迭代器类
与正向迭代器不一样的有 * 操作符,_pNode保存的是有效元素的下一个位置,如:想要的是_pNode->_pPre指向的元素,但是该迭代器保存的是_pNode的指针,还有++,–与正向迭代器相反。
其他操作与正向迭代器一致。

template<class T, class Ref, class Ptr>
class Reverse_ListIterator
{typedef ListNode<T>* PNode;typedef Reverse_ListIterator<T, Ref, Ptr> Self;
public:Reverse_ListIterator(const PNode& pNode = nullptr, const PNode& const P = nullptr) :_pNode(pNode), _P(P){}Ref operator*(){assert(_P != _pNode ->_pPre);return _pNode->_pPre->_val;}Ptr operator->(){return &(operator*());}Self& operator++(){_pNode = _pNode->_pPre;return *this;}Self operator++(int){Self tmp(_pNode);_pNode = _pNode->_pPre;return tmp;}Self& operator--(){_pNode = _pNode->_pNext;}Self& operator--(int){Self tmp(_pNode);_pNode = _pNode->_pNext;return tmp;}bool operator!=(const Self& l){return l._pNode != _pNode;}bool operator==(const Self& l){return l._pNode == _pNode;}PNode get(){return _pNode;}
private:PNode _pNode;PNode _P;
};

(3)list迭代器接口

//一些类型的重命名typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;typedef Reverse_ListIterator<T, T&, T*> reverse_iterator;typedef Reverse_ListIterator<T, const T&, const T*> reverse_const_iterator;// List Iterator//第一个有效元素位置的迭代器
iterator begin()
{return iterator(_pHead->_pNext,_pHead);
}
//最后一个有效元素位置的下一个位置的迭代器
iterator end()
{return iterator(_pHead,_pHead);
}
//加了const 修饰
const_iterator begin() const
{return const_iterator(_pHead->_pNext,_pHead);
}
const_iterator end()const
{return const_iterator(_pHead,_pHead);
}//反向迭代器
//哨兵位的位置reverse_iterator rbegin(){return reverse_iterator(_pHead,_pHead);}
//第一个有效元素位置reverse_iterator rend(){return reverse_iterator(_pHead ->_pNext,_pHead);}
//加了const修饰reverse_const_iterator rbegin() const{return reverse_const_iterator(_pHead,_pHead);}reverse_const_iterator rend()const{return reverse_const_iterator(_pHead->_pNext,_pHead);}

2、常用删除、插入接口

(1)insert
在迭代器位置前插入一个结点。

// 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T & val)
{//创造一个结点PNode tmp = new Node(val);//获取迭代器中的指针PNode _pos = pos.get();//进行插入PNode prv = _pos->_pPre;prv->_pNext = tmp;tmp->_pPre = prv;tmp->_pNext = _pos;_pos->_pPre = tmp;//返回新迭代器return iterator(tmp);
}

迭代器是否失效:
因为插入新的结点,不会影响到原来的结点,所以该迭代器不会失效。

(2)erase
删除迭代器位置结点。

// 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
{//判断是否为哨兵位结点iterator it = end();assert(pos != it);//获取迭代器结点指针PNode tmp = pos.get();//进行删除PNode next = tmp->_pNext;PNode prv = tmp->_pPre;prv->_pNext = next;next->_pPre = prv;delete tmp;tmp = nullptr;//返回被删除结点的下一个位置的结点迭代器return iterator(next);
}

迭代器是否失效:
因为将结点删除了,所以原本的迭代器是不能使用的,所以迭代器失效了。

(3)push_back、pop_back、push_front、pop_front
这里的头插、尾插、头删、尾删均复用上面两个函数接口。

void push_back(const T & val) { insert(end(), val); }
void pop_back() { erase(--end()); }
void push_front(const T & val) { insert(begin(), val); }
void pop_front() { erase(begin()); }

3、常用其他的一些函数接口

(1)size
返回大小,通过遍历链表即可找到。

size_t size()const
{//保存哨兵位的下一个位置PNode tmp = _pHead->_pNext;//开始遍历size_t count = 0;while (tmp != _pHead){tmp = tmp->_pNext;++count;}return count;
}

(2)empty
是否为空,判断哨兵位结点是否指向自己即可。

bool empty()const
{return _pHead == _pHead->_pNext;
}

(3)clear
清空链表,遍历链表逐个清空,保留哨兵位结点,再让哨兵位结点自己连接自己。

  void clear(){//保存有效结点位置PNode tmp = _pHead->_pNext;//遍历删除while (tmp != _pHead){PNode p = tmp->_pNext;delete tmp;tmp = p;}//重新指向_pHead->_pNext = _pHead;_pHead->_pPre = _pHead;

(4)swap
交换,只需要交换指向哨兵位结点的指针即可。
在这里插入图片描述

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

(4)front
获取第一个位置的元素。

T& front()
{assert(!empty());return _pHead->_pNext->_val;
}
const T& front()const
{assert(!empty());return _pHead->_pNext->_val;
}

(5)back
获取最后一个位置元素。

T& back()
{assert(!empty());return _pHead->_pPre->_val;
}
const T& back()const
{assert(!empty());return _pHead->_pPre->_val;
}

4、默认成员函数

(1)构造函数
构造函数都会先进行构造哨兵位结点,再进行下面的操作,除了无参构造,其他都复用了尾插,将元素尾插到链表结尾。

//无参构造
list() 
{   //构造一个哨兵位结点CreateHead(); 
}//利用n个val值进行构造
list(int n, const T& value = T())
{//构造一个哨兵位结点CreateHead();//将元素尾插入while (n != 0){push_back(value);--n;}
}//这里用迭代器区间构造,重写一个模板,使其可以使用其他容器的迭代器
template <class Iterator>
list(Iterator first, Iterator last)
{//构造一个哨兵位结点CreateHead();//将元素尾插入while (first != last){push_back(*first);++first;}
}//拷贝构造
list(const list<T>& l)
{//构造一个哨兵位结点CreateHead();//遍历+将元素尾插入PNode tmp = l._pHead->_pNext;while (tmp != l._pHead){push_back(tmp->_val);tmp = tmp->_pNext;}
}

(2)重载赋值运算符
通过传值传参构造一个临时容器 l ,再将其与原来的容器交换,当出了函数作用域之后临时容器就会调用析构函数,对临时容器的资源进行清理(就是原来容器的资源)。

list<T>& operator=(list<T> l)
{//与临时变量进行交换swap(l);return *this;
}

(3)析构函数
对链表清理。

~list()
{//清空链表clear();//删除哨兵位结点delete _pHead;_pHead = nullptr;
}

三、总代码

#pragma once
#include<iostream>
#include<assert.h>
#include<string>
using namespace std;namespace xu
{// List的结点类template<class T>struct ListNode{ListNode<T>* _pPre; //后继指针ListNode<T>* _pNext; //前驱指针T _val; //数据//构造结点ListNode(const T& val = T()) :_val(val), _pPre(nullptr), _pNext(nullptr){}};//List的正向迭代器类//Ref为T& Ptr为T*template<class T, class Ref, class Ptr>class ListIterator{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;public://构造函数 ,获取一个结点指针ListIterator(PNode pNode = nullptr) :_pNode(pNode){}Ref operator*(){return _pNode->_val;}Ptr operator->(){return &(operator*());}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self tmp(_pNode);_pNode = _pNode->_pNext;return tmp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self tmp(_pNode);_pNode = _pNode->_pPre;return tmp;}bool operator!=(const Self& l){return l._pNode != _pNode;}bool operator==(const Self& l){return l._pNode == _pNode;}PNode get(){return _pNode;}private:PNode _pNode;};//List的反向迭代器类template<class T, class Ref, class Ptr>class Reverse_ListIterator{typedef ListNode<T>* PNode;typedef Reverse_ListIterator<T, Ref, Ptr> Self;public:Reverse_ListIterator(PNode pNode = nullptr) :_pNode(pNode){}Ref operator*(){return _pNode->_pPre->_val;}Ptr operator->(){return &(operator*());}Self& operator++(){_pNode = _pNode->_pPre;return *this;}Self operator++(int){Self tmp(_pNode);_pNode = _pNode->_pPre;return tmp;}Self& operator--(){_pNode = _pNode->_pNext;}Self& operator--(int){Self tmp(_pNode);_pNode = _pNode->_pNext;return tmp;}bool operator!=(const Self& l){return l._pNode != _pNode;}bool operator==(const Self& l){return l._pNode == _pNode;}PNode get(){return _pNode;}private:PNode _pNode;};//list类template<class T>class list{typedef ListNode<T> Node;typedef Node* PNode;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;typedef Reverse_ListIterator<T, T&, T*> reverse_iterator;typedef Reverse_ListIterator<T, const T&, const T*> reverse_const_iterator;public://默认构造list() {   //构造一个哨兵位结点CreateHead(); }list(int n, const T& value = T()){//构造一个哨兵位结点CreateHead();//将元素尾插入while (n != 0){push_back(value);--n;}}template <class Iterator>list(Iterator first, Iterator last){//构造一个哨兵位结点CreateHead();//将元素尾插入while (first != last){push_back(*first);++first;}}list(const list<T>& l){//构造一个哨兵位结点CreateHead();//遍历+将元素尾插入PNode tmp = l._pHead->_pNext;while (tmp != l._pHead){push_back(tmp->_val);tmp = tmp->_pNext;}}list<T>& operator=(list<T> l){//与临时变量进行交换swap(l);return *this;}~list(){//清空链表clear();//删除哨兵位结点delete _pHead;_pHead = nullptr;}///// List Iteratoriterator begin(){return iterator(_pHead->_pNext);}iterator end(){return iterator(_pHead);}const_iterator begin() const{return const_iterator(_pHead->_pNext);}const_iterator end()const{return const_iterator(_pHead);}reverse_iterator rbegin(){return reverse_iterator(_pHead);}reverse_iterator rend(){return reverse_iterator(_pHead ->_pNext);}reverse_const_iterator rbegin() const{return reverse_const_iterator(_pHead);}reverse_const_iterator rend()const{return reverse_const_iterator(_pHead->_pNext);}///// List Capacitysize_t size()const{PNode tmp = _pHead->_pNext;size_t count = 0;while (tmp != _pHead){tmp = tmp->_pNext;++count;}return count;}bool empty()const{return _pHead == _pHead->_pNext;}// List AccessT& front(){assert(!empty());return _pHead->_pNext->_val;}const T& front()const{assert(!empty());return _pHead->_pNext->_val;}T& back(){assert(!empty());return _pHead->_pPre->_val;}const T& back()const{assert(!empty());return _pHead->_pPre->_val;}// List Modifyvoid push_back(const T & val) { insert(end(), val); }void pop_back() { erase(--end()); }void push_front(const T & val) { insert(begin(), val); }void pop_front() { erase(begin()); }// 在pos位置前插入值为val的节点iterator insert(iterator pos, const T & val){//创造一个结点PNode tmp = new Node(val);//获取迭代器中的指针PNode _pos = pos.get();//进行插入PNode prv = _pos->_pPre;prv->_pNext = tmp;tmp->_pPre = prv;tmp->_pNext = _pos;_pos->_pPre = tmp;//返回新迭代器return iterator(tmp);}// 删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos){//判断是否为哨兵位结点iterator it = end();assert(pos != it);//获取迭代器结点指针PNode tmp = pos.get();//进行删除PNode next = tmp->_pNext;PNode prv = tmp->_pPre;prv->_pNext = next;next->_pPre = prv;delete tmp;tmp = nullptr;//返回被删除结点的下一个位置的结点迭代器return iterator(next);}void clear(){//保存有效结点位置PNode tmp = _pHead->_pNext;//遍历删除while (tmp != _pHead){PNode p = tmp->_pNext;delete tmp;tmp = p;}//重新指向_pHead->_pNext = _pHead;_pHead->_pPre = _pHead;}void swap(list<T>& l){std::swap(_pHead, l._pHead);}private://让哨兵位结点指向自己void CreateHead(){_pHead = new  Node;_pHead->_pNext = _pHead;_pHead->_pPre = _pHead;}PNode _pHead;   //哨兵位结点};};

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

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

相关文章

卧式混料机:混合设备的智慧之选

卧式混料机&#xff0c;顾名思义&#xff0c;是一种采用卧式结构的混合设备。它的设计精巧&#xff0c;结构紧凑&#xff0c;不仅占用空间小&#xff0c;而且操作简便&#xff0c;维护方便。与传统的立式混料机相比&#xff0c;卧式混料机在混合效率、混合均匀度以及物料适应性…

DNS设置(linux)

1.配置dns需要现在/etc/sysconfig/network-scripts/目录下的ifcfg-ens33(后面数字也可能是其他的)中配置DNS 2.编辑/etc/resolv.conf文件&#xff0c;将上面网卡中加的dns服务器ip添加到此文件 vi /etc/resolv.conf重启网络配置 service network restart常用的dns的ip 国内…

香港优才计划申请时间要多久?各流程申请周期规划,再晚就来不及了!

香港优才计划申请时间要多久&#xff1f;各流程申请周期规划&#xff0c;再晚就来不及了&#xff01; 2024年是香港优才计划不限配额的最后一年&#xff0c;明年政策如何变化还未可知&#xff0c;但如果明年又设置限额了&#xff0c;那么今年最后的机会一定要抓住了。 在这里…

分享 - 树形dp

树形 d p dp dp 例1 - 基础 链接&#xff1a;树上子链 练手 分析 其实一看题就很显然的树形 d p dp dp子链在这里分为两种情况&#xff0c;如图黑链和红链 思路 d p [ i ] dp[i] dp[i] 表示以 i i i 开头的红链的最大权值易得&#xff1a; d p [ i ] m a x ( d p [ i…

祝贺!阿里云PolarDB斩获数据库国际顶会ICDE 2024工业赛道最佳论文

5月17日消息&#xff0c;在荷兰举行的国际顶级数据库学术会议ICDE 2024上&#xff0c;阿里云斩获工业和应用赛道的“最佳论文奖”&#xff0c;这也是中国企业首次获此殊荣。阿里云PolarDB创新性地解决了数据库Serverless中跨机事务迁移的核心难题&#xff0c;将跨机迁移时间压缩…

智能客服:论小红书商家杀出重围的正确姿势!

小红书「起飞」密码 洞悉需求&#xff0c;主动应变 面对众多的互联网平台&#xff0c;选择一个合适的平台宣传自家的品牌&#xff0c;也是一门学问&#xff0c;从“遇事不决&#xff0c;小红书”&#xff0c;这一 slogan 就能精准地捕捉了用户搜索行为的新趋势。 在过去的十…

【C++奇妙冒险】拷贝构造函数、运算符重载(赋值重载|const成员|取地址重载|const取地址重载)

文章目录 前言&#x1f6a9;拷贝构造函数&#x1fae7;概念&#x1fae7;特征&#x1fae7;默认生成的拷贝构造&#x1fae7;default关键字&#xff08;浅谈&#xff09; &#x1f6a9;运算符重载&#x1fae7;概念&#x1fae7;运算符重载注意事项&#x1fae7;封装如何保证&a…

如何使用GPT-4o?如何使用 GPT-4o API?

如何使用GPT-4o&#xff1f; GPT-4o 也可以通过 ChatGPT 界面使用 如何使用 GPT-4o API 新的 GPT-4o 模型遵循 OpenAI 现有的聊天完成 API&#xff0c;使其向后兼容且易于使用。 ​ 如何升级GPT4Plus&#xff1f; 升级ChatGPTPLSU4需要一张虚拟卡&#xff0c;点击获取​​​…

Java(六)——抽象类与接口

文章目录 抽象类和接口抽象类抽象类的概念抽象类的语法抽象类的特性抽象类的意义 接口接口的概念接口的语法接口的特性接口的使用实现多个接口接口与多态接口间的继承抽象类和接口的区别 抽象类和接口 抽象类 抽象类的概念 Java使用类实例化对象来描述现实生活中的实体&…

【第一节】从C语言到C++

目录 一、面向对象编程 1.早期概念 2.发展与普及 3. 现代发展 二、从C语言到C 1.关于堆内存的使用 2.关于函数重载 3.关于默认参数 4.引用 5.引用参数 6.作用域符号 三、C的输入输出机制 一、面向对象编程 面向对象编程&#xff08;Object-Oriented Programming&am…

如何进一步缩短Python性能

1、问题背景 给定一个(x,y)处的节点网格&#xff0c;每个节点有一个值(0…255)从0开始。有N个输入坐标&#xff0c;每个坐标在(0…x, 0…y)的范围内。一个值Z&#xff0c;它定义了节点的“邻域”。增加输入坐标及其邻居节点的值。网格边缘之外的邻居被忽略。基准案例&#xff…

产教协同|暴雨AI算力赋能传媒实践教学

近日&#xff0c;“第七届传媒实践教学创新研讨会暨中国高校影视学会实验教学专业委员会年会”在美丽的山城重庆成功举办。本次大会以“拥抱AI&#xff0c;融合共生”为主题&#xff0c;吸引了来自全国200多所高校、30多家企业以及700多位大视听实践教学产学研用各界专家、学者…

Data Lakehouse:你的下一个数据仓库

作者&#xff1a;张友东 StarRocks TSC member/镜舟科技 CTO 数据分析是现代企业和组织决策过程中不可或缺的一部分&#xff0c;数据分析技术经过数十年的发展&#xff0c;需求场景从 BI 报表到数据探寻、实时预测、用户画像等不断丰富&#xff0c;技术架构经历从数据仓库、数据…

【AIGC】GPT-4o技术分析-浅谈

GPT-4o&#xff1a;人工智能技术的全新里程碑 一、引言二、GPT系列版本间的对比分析三、GPT-4o的技术能力分析多模态处理能力速度与性能优化情感理解与表达能力 四、个人整体感受五、结语 一、引言 在人工智能技术的浪潮中&#xff0c;OpenAI再次以其卓越的创新能力引领潮流。近…

15.Redis之持久化

0.知识引入 mysql的事务,有四个比较核心的特性. 1. 原子性 2.一致性 3.持久性 >(和持久化说的是一回事)【把数据存储在硬盘 >持久把数据存储茌内存上>不持久~】【重启进程/重启主机 之后,数据是否存在!!】 4.隔离性~ Redis 是一个 内存 数据库.把数据存储在内存中的…

RFID芯片掼蛋牌:高科技与传统玩法结合,游戏体验焕然一新。

火爆“出圈”的掼蛋&#xff0c;是一种玩法相当鲜明的智力游戏。近年来得到了不少的推广和发展&#xff0c;各地举办了各种掼蛋比赛和活动&#xff0c;吸引了大量的参赛者和观众。此外&#xff0c;一些企业和机构也开始将掼蛋作为一种企业文化或者社交活动的方式&#xff0c;通…

灯下黑”挖出国内知名安全平台某BUF的CSRF漏洞

漏洞复现&#xff1a; 漏洞点在删除文章的地方&#xff0c;首先为了测试先发布一篇文章 发布之后我们可以查看文章&#xff0c;注意url中的一串数字&#xff0c;就是这篇文章的id&#xff0c;如下如&#xff1a; 这里的文章id是“271825”&#xff0c;首先抓一下删除文章的数据…

JavaScript基础(十)

上一篇学了各种数组方法&#xff0c;正好先做个练习回忆一下: 排序并去重 我随便写一组数&#xff0c;要求排好并去掉重复的: var arr [2,8,1,7,2,6,1,5,2,7,6,5]; for (var i0; i<arr.length; i){ for (var ji1; j<arr.length; j){ if(arr[i]arr[j]){ arr.splice(j,1)…

LeetCode 474.一和零

没做出来&#xff0c;最后看了解析&#xff0c;看了半天才懂。 我一开始把这个题当成多重背包来做了&#xff0c;因为有0和1两个参数需要考虑&#xff0c;但是中间很多情况不知道怎么处理。后面看了解析才知道这是个01背包问题&#xff0c;0和1都是一个物品上的属性&#xff0c…

K-means 聚类模型详解

K-means 聚类模型详解 K-means 是一种常用的无监督学习算法&#xff0c;用于将数据集分成 K 个簇。其目标是最小化各个簇内数据点到簇中心的距离平方和。K-means 广泛应用于图像压缩、市场细分、模式识别等领域。 算法步骤 初始化: 随机选择 K 个初始簇中心&#xff08;质心…