在单链表写入一组数据代码_数据结构之链表的实现

前言

学了数据结构之后,就用编程语言去实现它,这样理解才通透。同时地,学会刷题也是很重要,例如现在人尽皆知的leetcode。 我的目标就是进大厂,而必须要过的一关就是数据结构,慢慢学吧~   行百里者半九十。

我实现数据结构的代码是java,用什么语言不重要,主要是思路正确就可以了。所以你想学习c、c++也是一样的。

然后,这个博客系列是给小白看的,所以我有一些阅读建议:我会给出实现思路,先跟着我一起思考,虽然有些地方说的有些拗口,没办法,这是写作的缺点之一,所以要理解每一个地方说的概念后再往下,不然发现看不懂自然而然就“收藏夹吃灰”了

看懂了不一定会,要自己实现一下,你会发现巨多的坑等着你

不要急,慢慢来,你可以学会的(真的)

上一节我们提到了链表,那么我们这一篇就来实现其结构。

链表的定义:1)链表是以节点的方式来存储是链式存储

2)每个节点包含data域(存放的数据), next域:指向下一个节点,当next指向null时,可以表明才链表元素是尾结点

单向链表

首先,我们设计一个对象Node,对象里面只含有两个字段,分别是data域和next域

并添加上getter、setter方法,这个代码很简单class Node{

private String data;

private Node next;

public Node(String data, Node next){

this.data = data;

this.next = next;

}

public String getData(){

return data;

}

public void setData(String data){

this.data = data;

}

public Node getNext(){

return next;

}

public void setNext(Node next){

this.next = next;

}

@Override

public String toString(){

return "Node{" +

"data='" + data + '\'' +

'}';

}

}

接下来,就是设计API了,一个链表应该拥有插入、删除、打印、获取链表长度等功能。

首先,我们来讨论一下主要的API的功能:

插入:

我们要实现的功能是,在i元素之前插入某个新节点。如图:9a55d8271ca7b6bc0154104216b77e39.png

本来第 i 个节点是直接指向第 i+1 个节点的,由于来了新节点,我们就需要更改一下指向的顺序。

我们要做的具体步骤有三个:

① 找到第i个元素的节点

② 将该节点的next域指向我们传入的新节点

③ 将新节点的next域指向第 i+1 个节点

很简单,是不是?

不过,仔细一看上面这三个步骤,事情并没有那么简单。

我们想一想,如果实现了步骤②,那么到了步骤③,我们如何去表示 i 元素的 next 域呢? 当前状态下, i 元素的 next 域可是新节点噢(步骤②做的事情)。所以我们丢失了原先 i 节点的下一节点,怎么办呢

换个角度想一下,先实现步骤③就可以了。

我们先将新节点指向第 i+1个节点,然后再把 i 节点指向新节点,就可以了(链表当中,顺序很重要)。

删除

而删除的思路跟插入是差不多的,实现的效果是:传入第i个位置的上一个位置,然后删除它下一个节点,再连向下一节点的下一节点。

为什么不直接找到节点然后直接删除呢?因为该链表是单向的,没办法表示“当前节点的上一个节点”,所以我们无法让“上一个节点”与“下一个节点相连”。

所以思路是:从链表头节点一直往下找,找i -1次,就找到了所删除节点的上一节点。

好了,说完思路,我把完整代码贴出来:class singleLinkedList{

/**

* 传入一个头结点,遍历链表

*/

public void list(Node head){

Node temp = head.getNext();  // 头结点的下一节点

while (true) {

if (temp == null) {

// 找到最后节点,跳出循环

break;

}

System.out.println(temp);

temp = temp.getNext();

}

}

/**

* 传入参数i,删除第i个元素

*/

public Node delete(Node head, int i){

Node temp = head.getNext();  // 辅助节点, 指向了头结点的下一节点

for (int j = 1; j < i; j++) {

if (temp != null){

temp = temp.getNext();  // 辅助节点往后移动

}else{  // 说明temp.getNext() == null ,已经到了尾结点

System.out.println("链表长度不足"+i+",请重新传参");

return null;

}

}

// 跳出循环,表示找到要删除节点的上一节点

Node two_next = temp.getNext().getNext();  // 找到下一节点的下一节点,记为two_next

temp.getNext().setNext(null); // 将下一节点的值设置为空

temp.setNext(two_next);  // 将当前节点直接指向two_next

return head;

}

/**

* 传入参数i,在i-1的元素前面插入,node是要插入的节点

*/

public void insert(Node head, int i, Node node){

Node temp = head;  // 辅助节点

for (int j = 1; j < i; j++) {

if (temp == null) {

// 下一节点为空,表示链表长度不够

System.out.println("链表长度不足"+i+",请重新传参");

break;

}

else{

temp = temp.getNext();

}

}

// 跳出循环时,temp表示要插入节点位置的上一个节点

Node nextNode = temp.getNext();

node.setNext(nextNode);

temp.setNext(node);

}

/**

* 返回链表的长度

* @return

*/

public int getLength(Node head){

int count = 0;

Node temp = head.getNext();

while (temp != null){

temp =  temp.getNext();

count++;

}

return count;

}

}

汗...这样搞得我好像文章很长一样,这样会不会拉到这里的人变少了很多??不过当年写爬虫文章的时候,这么长的文是常有的事,哈哈。

然后,觉得写的还可以的,点个赞让我知道你来过好吗(卑微)

a739ef1ba60c42f0cacc586144e75f71.png

环形链表

将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表( circular linked list)。

循环链表如图所示:8fb668bb3dd51cdd7d05aebf3e5e893c.png

跟链表结构没有多大差别,只不过我们判断是否是尾结点的方式由 a.next == null 改成了 a.next == head(头结点)

当next域指向的是头结点时,我们认为当前节点为尾结点。

环形链表的好处:

当我们总是要获取最后一个节点时,可以使用环形链表。

首先实例化一个指向尾结点的变量,这意味着遍历时总是从尾结点开始找,对于前几个元素,只不过把操作数+1;而原本我们想要获取最后一个元素需要操作n次(链表节点个数的大小),而现在,有了环形链表,访问最后一个节点的操作数是1。

但是我们发现,这还是有些不够灵活,如果我们想要获取倒数第二个节点怎么办?

这时候,双向链表就起到了作用……

双向链表

双向链表,就是可以通过两个方向遍历链表,别看这图画的有些复杂,其实就是多了一个pre域(与next域相对的),用于指向上一个节点。133886e52a239171335c311bf9bb1031.png

双向链表的优点:

双向链表相对于单链表来说,要更复杂一些,不过,双向链表使得对某个结点的前后结点的操作,带来了方便,可以有效提高算法的时间性能。说白了,就是用空间来换时间。

对于双向链表的操作,也就是多了几步,但原理是一样的。下面举个添加节点的例子表明一下:d27dc16e1667569a2bfcd6ca40040b05.png

步骤①:将node(新节点)的pre域指向temp

步骤②:新节点指向下一节点

步骤③:下一节点的上一节点指向新节点

步骤④:当前节点的下一节点指向node

嗯…好啰嗦好啰嗦,简单来说,就是多了一个pre域,但是指向域的操作顺序是很重要的,不然就发生丢失节点的情况了(参考单向链表)。

添加代码实现:

假设,现在有个新节点node,temp为想要添加位置的前一个位置节点。嗯,为了文章篇幅,这里贴出的是伪代码,你们看懂了就好啦~

if (temp.next != null){   // 如果这个节点不是尾结点

temp = node.pre;  // 当前节点下一节点指向node(新节点)

node.next = temp.next;  // 新节点指向下一节点

temp.next.pre = node;  // 下一节点的上一节点指向新节点

node.pre = temp;  // 当前节点的下一节点指向node

}else{                  // 如果这个节点是尾结点

temp.next = node;

node.pre = temp;

}

写在后面

这个系列会一直写,后面的还有数组、队列、还有我还没学的二叉树等等。

对于知识点梳理的同时,又创作了一篇博客,还有各位小伙伴点的赞,这给我带来无穷大的动力。

冲冲冲!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值