主要参考:《数据结构与算法/leetcode/lintcode题解》、《胡伟煌 数据结构 学习笔记》
总结一些在C++、Java、Python中对于字符串的一些常见操作。
首先是Python。先看一下Python中内置的字符串类型的方法都有哪些(带下划线的我们通常不用的所以过滤掉了):
In[19]: ls=[x for x in str.__dict__ if not x.endswith("_") ];
In[19]: ls.sort()
In[19]: print(ls)
Out[19]: ['capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
更详细的信息可以通过help(str)
来获取。
主要的函数有:
'.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
center
,但它是左对齐center
,它是右对齐partition
,但它是从右往左的split
,但它是从右往左lstrip
,但它是去除尾部的空白字符或者指定字符lstrip
,但它是同时删除首部或尾部的空白符或者指定字符'0'
使字符串达到指定长度并返回基本上就是这些,用法都非常简单的,都是很基本的字符串操作。
然后是 Java。对于Java,要了解它的一些基本类型的常见用法,最直接的方法是去找它的官方文档。https://docs.oracle.com/en/java/javase/14/docs/api/index.html。
一些字符串相关的类型:String
、StringBuffer
、StringBuilder
都定义在java.lang
模块中,String是基本的字符串类型,StringBuffer是线程安全的长度可变的字符串,而StringBuilder不是线程安全的,但通常单线程下效率更高,所以一般用StringBuilder。
相关的构造函数和方法官网中列出了很多。
示例如下
package algo_study;
public class string {
public static void main(String[] args) {
String str="Hello world";
String str1=new String(new char[]{'a','b','c'});
String str2=new String(new char[]{'a','b','c','d'},1,3);
String str3=new String("Hello world");
String str4=new String(new StringBuffer("stringbuffer"));
String str5=new String(new StringBuilder("stringbuilder"));
System.out.println("str--"+str);
System.out.println("str2--"+str1);
System.out.println("str3--"+str2);
System.out.println("str4--"+str3);
System.out.println("str5--"+str4);
System.out.println("str6--"+str5);
System.out.println(str==str3);//false
System.out.println(str.equals(str3));//true
char ch=str.charAt(1);
int ind=str.indexOf('l');
//StringBuilder
StringBuilder sbui1=new StringBuilder();
StringBuilder sbui2=new StringBuilder(10);
StringBuilder sbui3=new StringBuilder(str);
System.out.println("sbui1--"+sbui1);
System.out.println("sbui2--"+sbui2);
System.out.println("sbui3--"+sbui3);
sbui3.append(str);
sbui3.append(sbui3);
System.out.println("sbui3--"+sbui3);
sbui1=sbui3.delete(4,8);
System.out.println("sbui1--"+sbui1);
sbui2=sbui3.deleteCharAt(5);
System.out.println("sbui2--"+sbui2);
System.out.println(sbui3.insert(0,"test"));
System.out.println(sbui3.reverse());
System.out.println(sbui3.replace(0,2,"test"));
sbui3.setCharAt(0,'x');
System.out.println(sbui3);
System.out.println(sbui3.substring(3,6));
System.out.println(sbui3.subSequence(4,7));
}
}
输出
str--Hello world
str2--abc
str3--bcd
str4--Hello world
str5--stringbuffer
str6--stringbuilder
false
true
sbui1--
sbui2--
sbui3--Hello world
sbui3--Hello worldHello worldHello worldHello world
sbui1--HellrldHello worldHello worldHello world
sbui2--HellrdHello worldHello worldHello world
testHellrdHello worldHello worldHello world
dlrow olleHdlrow olleHdlrow olleHdrlleHtset
testrow olleHdlrow olleHdlrow olleHdrlleHtset
xestrow olleHdlrow olleHdlrow olleHdrlleHtset
tro
row
进程已结束,退出代码0
C++中的字符串string
作为一种顺序容器,随机访问块,尾部插入、删除块。很多顺序容器能用的操作string 也能用。完全可以把string
理解成一个储存char
类型元素的一个vector
容器,当然其中有些细节是需要注意的。
相比较而言,C++中的字符串操作会稍微复杂一些,也容易出错,但还是挺好用的,而且执行效率高。主要的一些操作如下
#include
#include
int main()
{
std::string str;
std::string str1{ 'a','b','c' };
std::string str2(str1);
std::string str3(str1.begin()+1, str1.end());
std::cout << "size of str1: " << str1.size() << std::endl;
str3.swap(str1);//拷贝
std::cout << str3 << std::endl;
str2 = "test";
str3.assign(str2);//拷贝
std::cout << str3 << std::endl;
str1.assign(str2.begin(), str2.end());
std::cout << str1 << std::endl;
str1.push_back('t');//尾部添加一个字符
std::cout << str1 << std::endl;
str1.insert(str1.begin(), 5, 'x');//插入字符
std::cout << str1 << std::endl;
str1.insert(str1.begin(), str2.begin(), str2.end());
std::cout << str1 << std::endl;
str1.insert(str1.begin(), { 't','e','s','t' });
std::cout << str1 << std::endl;
std::cout << str1.at(0) << std::endl;
std::cout << str1[0] << std::endl;
str1.pop_back();//返回尾部字符并删除
std::cout << str1 << std::endl;
str1.erase(str1.begin());//删除迭代器指向的元素
std::cout << str1 << std::endl;
str1.erase(str1.begin(), str1.begin() + 3);
std::cout << str1 << std::endl;
str1.clear();//删除所有的元素
std::cout << str1 << std::endl;
std::string str4({'t','e','s','t'}, 2);
std::string str5("The world", 4);
std::string str6(str5, 2, 6);
std::cout << str4 << std::endl;
std::cout << str5 << std::endl;
std::cout << str6 << std::endl;
str2 = "hello world";
str1 = str2.substr(0, 6);
std::cout << str1 << std::endl;
str1.append(str2);
std::cout << str1 << std::endl;
str1.replace(8, 3, "test");
std::cout << str1 << std::endl;
auto ind1 = str1.find('t', 1);//搜索
auto ind2 = str1.rfind('t', 1);
auto ind3 = str1.find(str2,1);
auto ind4 = str1.find_first_of(str2, 1);
auto ind5 = str1.find_first_not_of(str2, 1);
auto ind6 = str1.find_last_of(str2, 1);
auto ind7 = str1.find_first_not_of(str2, 1);
std::cout << ind1 << " -" << ind2 << " -" << ind3 << " -"
<< ind4 << " -" << ind5 << " -" << ind6 << " -" << ind7 << " -" << std::endl;
int i = 12345;
std::string s1 = std::to_string(i);
double d = std::stod(s1);
std::string s2 = "pi=3.141592654";
d = stod(s2.substr(s2.find_first_of("+-.0123456789")));
std::cout << "d=" << std::endl;
return 0;
}
输出
size of str1: 3
abc
test
test
testt
xxxxxtestt
testxxxxxtestt
testtestxxxxxtestt
t
t
testtestxxxxxtest
esttestxxxxxtest
testxxxxxtest
st
The
e
hello
hello hello world
hello hetest world
8 -4294967295 -4294967295 -1 -8 -1 -8 -
d=3.14159
C:\Users\xhh\Source\Repos\algo_study1\Debug\algo_study1.exe (进程 9680)已退出,代码为 0。
按任意键关闭此窗口. . .
基本概念:
首先是四种基本的存储结构:顺序存储、链式存储、索引存储、散列存储。
线性表是由n个节点组成的有限序列,满足特征:1)每个节点之多只有一个前驱节点且至多只有一个后继节点;2)起始节点没有前驱节点;3)终结节点没有后继节点。
线性表支持的基本运算有:1)初始化线性表;2)求线性表的长度;3)求线性表的第i个元素;4)按值查找元素,返回元素序号;5)插入元素;6)删除元素;7)输出列表。
线性表有两种存储方式:顺粗存储和链式存储。
而链表就是链式存储的线性表。根据指针域的不同,链表可以分为单向链表、双向链表、循环链表等。
要想理解清楚链表的程序实现,我觉得做好还是先在C++或者C语言上实现,因为很多语言是没有指针的概念的,这样操作起来感觉会容易造成思维混乱,而C/C++上实现就比较思路清晰。而且感觉其它语言像python或者Java都是经过封装的,大多数时候都有现成的直接用。
首先是单链表。单链表的一个节点定义如下:
//单链表的一个节点包含一个变量和指向下一个节点的指针
struct listNode
{
int val;
listNode* next;
listNode(int v, listNode* n = nullptr) :val(v), next(n) {}
};
难点是将一些常见的对链表的操作包装到一个类中,一些基本的概念都理解的前提下,要实现这些基本操作还是需要费一番功夫的。因为指针本身还是很抽象的概念,即使完全理解了它的作用机制,但要利用指针来实现一些简单操作至少对于初学者来说是很有难度的,必须时刻注意一个指向某种类型的指针的变量,我们什么时候需要用到这个指针变量指向的元素,什么时候需要它的地址(指针本质上就是一个地址,通过赋值改变它的地址也就改变了它指向的元素)。
下面是我个人完成的一个类的定义,实现了前面列出的7个基本运算。这个类可以作为一个容器。
#include"ListNode.cpp"
#include
class linkedList {
private:
listNode* head;//头部,注意,头部不用来储存数据,只是作为起始点
int length;//长度
public:
//friend std::ostream& operator<<(std::ostream& out, linkedList& ll);
linkedList() :head(new listNode(0)), length (0){} //构造函数,初始化
~linkedList() { delete head; } //析构函数
int size() { return this->length; } //求长度
const int operator[](int n) const{//求 //求链表中的某个值
if (n < length) {
listNode* pNode = head;
for (int i = 0; i <= n; ++i)
pNode = pNode->next;
return pNode->val;
}
else
return 0;
std::cerr << "error:subscript overflow."<<std::endl;
}
size_t find(int i) { //按值查找元素并返回下标
listNode* pNode = head->next;
int ind = 0;
while (pNode != nullptr) {
if (i == pNode->val)
return ind;
else {
pNode = pNode->next;
ind++;
}
}
return -1;//没找到
}
void insert(int ind,int n) { //插入一个值
listNode* ins = new listNode(n);
if (ind <= length &&ind>=0 ) {
listNode* pNode;
pNode = head;
for (int i = 0; i < ind; ++i)
pNode = pNode->next;
ins->next = pNode->next;
pNode->next = ins;
length++;
}
else if (ind == -1) {
listNode* p ;
p = head;
while (p->next != nullptr)
p = p->next;
p->next = ins;
this->length ++;
//std::cout << length << std::endl;
}
else {
std::cerr << "error: invalid subscript,insert data failed"<<std::endl;
}
}
void pushback(int n) { //添加到链表末尾
insert(-1, n);
}
void erase(int ind) { //删除元素
if (ind < length && ind >= 0) {
listNode* pNode = head;
for (int i = 0; i < ind; ++i)
pNode = pNode->next;
pNode->next = pNode->next->next;
this->length--;
}
else
std::cerr << "error:invalid subscript,erase data failed!" << std::endl;;
}
listNode* HEAD(){
return head;
}
void printThis() { //输出整个链表中的数据
listNode* p = head;
while(p->next!=nullptr) {
p = p->next;
std::cout << p->val<<", ";
}
std::cout << std::endl;
}
void reverse() { //反转链表
listNode* pro, * pn = nullptr,*temp;
pro = head->next;
while (pro!= nullptr) {
temp = pro->next;
pro->next = pn;
pn = pro;
pro = temp;
}
head->next = pn;
}
};
然后是使用这个类来具体的实现我们需要的操作:
#include
#include
#include"linkedList.cpp"
std::ostream& operator<<(std::ostream& out, linkedList& ll) { //输出整个链表,注意双目运算符必须在类之外定义
listNode* pNode;
pNode = ll.HEAD();
while (pNode->next != nullptr) {
pNode = pNode->next;
out << pNode->val << ", ";
}
return out;
}
int main()
{
linkedList ll;
std::cout << "链表的长度:"<<ll.size() << std::endl;
for (int i = 0; i < 10; ++i)
ll.pushback(i*i);
std::cout << "链表的长度:" << ll.size() << std::endl;
std::cout << "ll[3]=" << ll[3] << std::endl;
std::cout << "查找 16,下标:" << ll.find(16) << std::endl;
std::cout << "ll的所有元素:" << std::endl;
ll.printThis();
ll.insert(0, 1111);
std::cout << "ll的所有元素:" << std::endl;
ll.printThis();
ll.erase(1);
ll.printThis();
ll.reverse();
ll.printThis();
std::cout << ll << std::endl;
return 0;
}
输出
链表的长度:0
链表的长度:10
ll[3]=9
查找 16,下标:4
ll的所有元素:
0, 1, 4, 9, 16, 25, 36, 49, 64, 81,
ll的所有元素:
1111, 0, 1, 4, 9, 16, 25, 36, 49, 64, 81,
1111, 1, 4, 9, 16, 25, 36, 49, 64, 81,
81, 64, 49, 36, 25, 16, 9, 4, 1, 1111,
81, 64, 49, 36, 25, 16, 9, 4, 1, 1111,
C:\Users\xhh\Source\Repos\algo_study1\Debug\algo_study1.exe (进程 14848)已退出,代码为 0。
按任意键关闭此窗口. . .
用C++来实现链表还是很有意义的。另外也可以尝试用C语言来实现链表,不过感觉和C++的代码会比较类似的。
同样的思路用Java写就简单了很多,自己实际操作起来就可以明显感受到C++和Java之间的一些差别,用C++的时候还debug了好久,当然可能一开始还在构思其中的逻辑,但用Java写直接能正常运行都没有出现Bug还是让我有点意外的。虽然二者的总的代码行数还是差不多的,但和C++相比,Java仿佛有一种简洁的美感,就很神奇。注意到,Java舍弃了C++中的可以操作符重载的特性,这样提高了可读性,也更不容易出错了,这一点是很不错的。前面我用C++实现链表的时候很长的事件都在纠结操作符重载的问题。
然而,虽然Java没有指针的概念,但似乎并不影响它去实现链表,反而使这个过程更加的简便。表面是它使没有指针的概念的,但它的底层肯定使需要借助相关的概念来实现。我们理解上是要知道其中有指针的操作。
首先是定义节点
package algo_study;
public class listNode {
public int val;
public listNode next;
public listNode(int val){
this.val=val;
this.next=null;
}
}
然后我把链表的类的定义和具体使用放一起了。
package algo_study;
public class linkedList {
private listNode head;
private int length;
public linkedList(){
this.head=new listNode(0);
this.length=0;
}
public int getLength() { //获取链表长度
return length;
}
public int at(int ind){ //返回链表中的第几个值,Java中并不支持运算符重载,没法像C++那样玩花的~
listNode ln=head;
if(ind >= 0 && ind < length){
for(int i=0;i<=ind;++i)
ln=ln.next;
return ln.val;
}else if(ind==-1){
while(ln.next!=null)
ln=ln.next;
return ln.val;
}else{
System.out.println("error subscript");
return 0;
}
}
public int find(int i){ //查找某个值并返回下标
listNode ln=head.next;
int ind=0;
while(ln!=null){
if(i==ln.val)
return ind;
else{
ln=ln.next;
ind++;
}
}
return -1;
}
public void insert(int ind,int n){ //插入数据
listNode ln=new listNode(n);
if(ind<=length&&ind>=0){
listNode ln1=head;
for(int i=0;i<ind;++i)
ln1=ln1.next;
ln.next=ln1.next;
ln1.next=ln;
length++;
}else if(ind==-1){
listNode p=head;
while(p.next!=null)
p=p.next;
p.next=ln;
length++;
}else
System.out.println("error:invalid subscript, insert data fained");
}
public void pushBack(int n){
insert(-1,n);
}
public void erase(int ind){ //删除数据
if(ind<length &&ind>=0){
listNode pNode=head;
for(int i=0;i<ind;++i)
pNode=pNode.next;
pNode.next=pNode.next.next;
this.length--;
}else if(ind==-1){
listNode pNode=head;
while(pNode.next.next!=null)
pNode=pNode.next;
pNode.next=null;
length--;
}else
System.out.println("error:erase date failed");
}
@Override
public String toString() { //重载这个方法方便打印链表的所有数据
StringBuilder str=new StringBuilder("[");
listNode ln=head;
while(ln.next!=null){
ln=ln.next;
str.append(" "+ln.val);
}
str.append(" ]");
return str.toString();
}
public void reverse(){ //反转链表
listNode pro,pn=null,temp;
pro=head.next;
while(pro!=null){
temp=pro.next;
pro.next=pn;
pn=pro;
pro=temp;
}
head.next=pn;
}
public static void main(String[] args) {
linkedList LL=new linkedList();
System.out.println(LL.getLength());
for(int i=0;i<10;++i)
LL.pushBack(i*i);
System.out.println(LL.getLength());
System.out.println("LL: "+LL);
LL.insert(1,1234);
System.out.println(LL);
LL.erase(2);
System.out.println(LL);
LL.reverse();
System.out.println(LL);
}
}
输出
0
10
LL: [ 0 1 4 9 16 25 36 49 64 81 ]
[ 0 1234 1 4 9 16 25 36 49 64 81 ]
[ 0 1234 4 9 16 25 36 49 64 81 ]
[ 81 64 49 36 25 16 9 4 1234 0 ]
进程已结束,退出代码0
python用起来又是完全不同的感觉了,整个过程是,先考虑了半天完成C++版本的,然后照着C++的版本用Java写,最后照着Java的版本来写python。由于是照着写的,没有多加考虑,也没有充分发挥python代码简洁的特性。过程中基本也没有遇到什么困难。python的一些高级特性比如装饰器我一直没有去认真研究,有些细节甚至可以自己猜到,比如这里的怎么让内置的print
函数打印我们定义类输出我们想要的结果,在没有特意查资料且以前没遇到这个问题的情况下,我直接猜到了是在类中定义__str__
函数,并让它返回字符串。
整体下来,感觉还是Java的使用体验最好。C++优点太复杂了,python又比较松散不严谨,而Java就是恰到好处的。
首先是节点定义:
class listNode:
def __init__(self,v):
self.val=v
self.next=None
然后是链表的实现
from algo_study import listNode
class linkedList:
def __init__(self):
self.head=listNode.listNode(0)
self.length=0
def size(self):
return self.length
def at(self,ind):
ln=self.head
if ind>=0 and ind<self.length:
for x in range(ind+1):
ln=ln.next
return ln.val
elif ind==-1:
while ln.next!=None:
ln=ln.next
return ln.val
else:
print("Error")
def find(self,i):
ln=self.head.next
ind=0
while ln!=None:
if(i==ln.val):
return ind
else:
ln=ln.next
ind+=1
return -1
def insert(self,ind,n):
ln=listNode.listNode(n)
if ind<=self.length and ind>=0:
ln1=self.head
for i in range(ind):
ln1=ln1.next
ln.next=ln1.next
ln1.next=ln
self.length+=1
elif ind==-1:
p=self.head
while(p.next!=None):
p=p.next
p.next=ln
self.length+=1
else:
print("error")
def pushBack(self,n):
self.insert(-1,n)
def erase(self,ind):
if ind in range(self.length):
p=self.head
for i in range(ind):
p=p.next
p.next=p.next.next
self.length-=1
elif ind ==-1:
p=self.head
while p.next.next!=None:
p=p.next
p.next=None
self.length-=1
else:
print("error")
def toString(self):
str1,p="[",self.head
while p.next!=None:
p=p.next
str1+=" "+str(p.val)
str1+=" ]"
return str1
def reverse(self):
pro,pn,temp=self.head.next,None,None
while(pro!=None):
temp=pro.next
pro.next=pn
pn=pro
pro=temp
self.head.next=pn
def __str__(self):
return self.toString()
LL=linkedList()
print(LL.size())
for x in range(10):
LL.pushBack(x*x)
print(LL.size())
print(LL.toString())
LL.insert(1,1234)
print(LL)
LL.erase(2)
print(LL)
LL.reverse()
print(LL)
输出
0
10
[ 0 1 4 9 16 25 36 49 64 81 ]
[ 0 1234 1 4 9 16 25 36 49 64 81 ]
[ 0 1234 4 9 16 25 36 49 64 81 ]
[ 81 64 49 36 25 16 9 4 1234 0 ]
双向链表的话对于每个节点都有两个指针prev
和next
,分别指向前一个节点和后一个节点,头部的prev
为空指针,尾部的next
为空指针,相比较单向链表,它可以更快速的操作双端的元素。理解了单向链表的话双向链表并没有什么难的。
虽然能理解个大概,但具体的细节要把握准确还是有一定的难度的。需要很灵活的处理实际遇到的情况。
像这里我们要实现双向链表的一些操作,那么包括查找、插入、删除、反转等操作和单链表都是很不一样的,这时也是很容易出错的。
首先是双向链表的节点类定义,我现在才发现Visual Stdio 2019新建一个C++类时它会自动生成对应的头文件和cpp源文件,当然同时用到头文件和源文件来定义一个类才是最规范的定义类的格式,但通常我们定义一个简单的类是是直接在源文件中定义的,都不会用到头文件,实际上头文件里的语法也是按照C++语法规范的,但这种头文件加源文件的定义一个类的方式应该是官方所推荐的。试了一下,这种方式确实很不错,尤其是对于一些大型项目,这种方式还是很有用的。
dListNode.h
#pragma once
class dListNode
{
public:
int val;
dListNode* prev, * next;
dListNode(int v);
};
dListNode.cpp
#include "dListNode.h"
dListNode::dListNode(int v):val(v), prev(nullptr), next(nullptr) {}
然后是双向链表的实现
dLinkedList.h
#pragma once
#include "dListNode.h"
class dLinkedList
{
private:
dListNode* head, *tail; //头部和尾部,不储存具体数值,仅作为标志
int length;
public:
dLinkedList();
~dLinkedList();
int size();//大小
const int operator[](int n)const;//at
int find(int i);//搜素
void insert(int ind,int n);//插入
void pushback(int n);//末尾
void add(int n);//添加到头部
void erase(int ind);//删除
int pop();//弹出尾部的元素
int popHead();//弹出首个的元素
void printThis();//打印所有的元素
void reverse();//反转链表
};
dLinkedList.cpp
#include "dLinkedList.h"
#include
//构造函数,注意初始时要求head的next指向tail,tail的prev指向head
dLinkedList::dLinkedList() :head(new dListNode(0)), tail(new dListNode(0)), length(0) { head->next = tail; tail->prev = head; }
dLinkedList::~dLinkedList() { delete head, delete tail; }
int dLinkedList::size() { return length; }
const int dLinkedList::operator[](int n) const
{
dListNode* p1 = head,*p2=tail;
if (n < 0)
n += length; //允许下标去负数,如-1->length-1
if (n < length && n >= 0) {
if (n <= length / 2) { //判断我们访问的元素是靠近头部还是靠近尾部,分别采取两个方向来接近该元素,
for (int i = 0; i <= n; ++i) //这样可以充分利用双向链表的特性加快访问速度
p1 = p1->next;
return p1->val;
}
else {
for (int i = length; i > n; --i)
p2 = p2->prev;
return p2->val;
}
}
return 0;
}
int dLinkedList::find(int i)
{
dListNode* p = head->next;
int ind = 0;
while (p ->next!= nullptr) {
if (i == p->val)
return ind;
else {
p = p->next;
ind++;
}
}
return -1;
}
void dLinkedList::insert(int ind, int n)
{
dListNode* p1 = head, * p2 = tail,*pv=new dListNode(n);
if (ind < 0)
ind += length;
if (length == 0 && ind == -1)
ind = 0;
if (ind <= length && ind >= 0) {
if (ind <= length / 2) {
for (int i = 0; i < ind; ++i)
p1 = p1->next;
pv->next = p1->next;//从中间插入一个值,需要修改四个指针的指向,这里一定要注意
pv->prev = p1;
p1->next->prev = pv;
p1->next = pv;
}
else {
for (int i = length-1; i > ind; i--)
p2 = p2->prev;
pv->next = p2;
pv->prev = p2->prev;
p2->prev->next = pv;
p2->prev = pv;
}
length++;
}
else
std::cerr << "insert failed!" << std::endl;
}
void dLinkedList::pushback(int n)
{
insert(-1, n);
}
void dLinkedList::add(int n)
{
insert(0, n);
}
void dLinkedList::erase(int ind)
{
dListNode* p1 = head, * p2 = tail;
if (ind < 0)
ind += length;
if (ind < length && ind >= 0) {
if (ind <= length / 2) {
for (int i = 0; i < ind; ++i)
p1 = p1->next;
p1->next->prev = nullptr;
p1->next = p1->next->next;
p1->next->prev = p1;
}
else {
for (int i = length-1; i > ind; --i)
p2 = p2->prev;
p2->prev->next = nullptr;
p2->prev = p2->prev->prev;
p2->prev->next = p2;
}
length--;
}
else
std::cerr << "erase failed!" << std::endl;
}
int dLinkedList::pop()
{
int i = tail->prev->val;
erase(-1);
return i;
}
int dLinkedList::popHead()
{
int i = head->next->val;
erase(0);
return i;
}
void dLinkedList::printThis()
{
std::cout << "[";
dListNode* p1 = head;
while (p1->next->next != nullptr) {
p1 = p1->next;
std::cout << " " << p1->val;
}
std::cout << "]" << std::endl;
}
void dLinkedList::reverse()
{
dListNode* p1 = head->next, * p2 = tail->prev, * temp = nullptr, * pn = tail;
tail->prev = head->next;
while (p1->next!= nullptr) {
temp = p1->next;
p1->next = pn;
p1->prev = temp;
p1 = temp;
pn = temp->prev;
}
head->next = p2;
}
测试只需要在原来的main函数中稍微修改一下就可以了
#include
#include
#include"linkedList.cpp"
#include"dlinkedList.h"
int main()
{
dLinkedList ll;
std::cout << "链表的长度:"<<ll.size() << std::endl;
for (int i = 0; i < 10; ++i)
ll.pushback(i*i);
std::cout << "链表的长度:" << ll.size() << std::endl;
std::cout << "ll[3]=" << ll[3] << std::endl;
std::cout << "查找 16,下标:" << ll.find(16) << std::endl;
std::cout << "ll的所有元素:" << std::endl;
ll.printThis();
ll.insert(0, 1111);
std::cout << "ll的所有元素:" << std::endl;
ll.printThis();
ll.erase(1);
ll.printThis();
ll.reverse();
ll.printThis();
//std::cout << ll << std::endl;
return 0;
}
输出
链表的长度:0
链表的长度:10
ll[3]=9
查找 16,下标:4
ll的所有元素:
[ 1 4 0 9 16 25 36 49 64 81]
ll的所有元素:
[ 1111 1 4 0 9 16 25 36 49 64 81]
[ 1111 4 0 9 16 25 36 49 64 81]
[ 81 64 49 36 25 16 9 0 4 1111]
C:\Users\xhh\Source\Repos\algo_study1\Debug\algo_study1.exe (进程 50696)已退出,代码为 0。
按任意键关闭此窗口. . .