带头双向循环链表

带头双向循环链表的概念

        双向链表是一个结构体内有两个指针,一个指针指向头,一个指向尾。所以给双向链表一个位置,可以很轻松的访问前面的节点与后面的节点。双向链表的结构复杂,但是代码实现会有很有的优势,实现反而简单。

        今天简单的实现一下带头双向循环链表的增删查改与初始化。

双向链表的初始化与结构体定义

结构体定义

        因为叫双向链表所以需要两个指针,一个指向前面的节点,一个指向后面的节点。

typedef struct ListNode
{
	struct ListNode* prev;
	struct ListNode* next;
	LTDateType data;
	
}ListNode;

        初始化

        带头双向循环列表需要一个哨兵位,所以进行初始化的时候需要范围链表哨兵位的地址。可以进行二级指针传参,直接修改地址,因为接下来的代码都采用一级指针,为了整体规范性,没有采用二级指针。链表结构为循环,进行初始化的时候要让他自己指向自己。图与代码如下:

ListNode* ListCreate()
{
	ListNode* newnode = NewNode();
	newnode->prev = newnode;
	newnode->next = newnode;
	return newnode;
}

双向链表的头插

        对双向链表进行头插需要开辟一个新的节点:

        让哨兵位head指向新节点newnode,让newnode指向next,next指向newnode。实现如下图的连接:

在这里为什么定义了一个next的变量呢?

        因为定义了next的变量后,可以不用考虑顺序,直接指向newnode与head,如果不定义next的变量,就是用newnode与head的话,需要考虑指向的先后顺序,防止陷入自己指向自己。

代码如下:

        单向链表与双向的区别

        双向链表的头插与单向不带头不循环链表有一个蛮大的区别:

        双向链表的头插不需要考虑空指针,可以直接指向下一个节点,单向不带头不循环链表需要考虑刚开始进来的指针是空指针的问题,而且需要用二级指针改变地址。单向链表的代码如下:

双向链表的尾插

        尾插与头插差别不是很大,定义一个tail指针变量:

       将next的指针变量等于tail的地址。就可以不用按照顺序的进行指向。代码如下:

双向链表的头删

      

        对链表进行头部的删除,最方便快捷的方式就是定义两个变量,一个记录头部的位置(准备释放空间),一个记录新头部的位置。这样对链表进行头删的时候可以不考虑顺序,直接进行指向。另外,因为链表节点的地址都是动态开辟的,删除的时候需要释放。

        链表头删与尾删都需要注意的一点是:链表只有哨兵位的情况下,是不用进行删除的,所以要进行一个判断,判断该链表是不是非空。非空就进行删除。链表空的时候heap也在其中。也就是链表为空不计算head。

图如下:

代码如下:

        因为链表的头删与尾删实现的思想是差不多的,所以这里不再介绍尾删了。

双向链表的查找

         对链表查找就需要对链表进行遍历。对链表进行遍历的话,他的结束条件是等于哨兵位。所以我们需要创建一个变量。如图:

        令该变量等于head的下一个节点,当值等于所要查找的时候,就让他进入下一个节点进行查找。

        当cur与head进行重合就说明里面没有要查找的值,结束循环。代码如下:

双向链表的插入 

        在某一个位置前进行插入数据。双向链表虽然结构复杂,但是很好进行代码编写。在某一个为之前,直接去查找节点的prev就可以记录之前得到位置。然后进行指向的更改即可。图如下:

        first指针变量就是pos->prev中存放的地址。

        更改后的图:

        代码如下:

        当我们写出这段代码后,可以对头插,尾插代码进行更改,在头插、尾插函数中调用这个函数一次性进行实现。

            用该代码进行头插就是在pos=head->next位置前插入。图如下:

        更改后如下:

        

        代码如下:

                尾插就是pos=head进行插入。

        图如下:

        因为是循环,尾的后面就是头,那么头的后面就是尾。

代码如下:

双向链表的打印

        链表进行打印的结束条件就是不能等于哨兵位。等于哨兵位说明链表要么为空,要么打印完成。

        代码如下:

双向链表的销毁 

        既然是动态开辟的空间,就需要进行空间销毁。不然会内存泄露。 

       对空间进行释放后 ,记得在函数外手动置空。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是Java实现带头双向循环链表的完整源码,供参考: ``` public class DoublyCircularLinkedList<T> { private Node<T> head; // 头节点 // 节点类 private static class Node<T> { T data; Node<T> prev; Node<T> next; Node(T data) { this.data = data; this.prev = null; this.next = null; } } // 构造函数 public DoublyCircularLinkedList() { head = new Node<>(null); head.prev = head; head.next = head; } // 在链表末尾添加元素 public void add(T data) { Node<T> node = new Node<>(data); node.prev = head.prev; node.next = head; head.prev.next = node; head.prev = node; } // 在指定位置插入元素 public void insert(int index, T data) { Node<T> node = new Node<>(data); Node<T> p = head.next; int i = 0; while (p != head && i < index) { p = p.next; i++; } if (p == head || i > index) { throw new IndexOutOfBoundsException(); } node.prev = p.prev; node.next = p; p.prev.next = node; p.prev = node; } // 删除指定位置的元素 public void remove(int index) { Node<T> p = head.next; int i = 0; while (p != head && i < index) { p = p.next; i++; } if (p == head || i > index) { throw new IndexOutOfBoundsException(); } p.prev.next = p.next; p.next.prev = p.prev; p.prev = null; p.next = null; } // 获取指定位置的元素 public T get(int index) { Node<T> p = head.next; int i = 0; while (p != head && i < index) { p = p.next; i++; } if (p == head || i > index) { throw new IndexOutOfBoundsException(); } return p.data; } // 获取链表长度 public int size() { Node<T> p = head.next; int size = 0; while (p != head) { size++; p = p.next; } return size; } } ``` 该代码实现了带头双向循环链表数据结构,支持在链表末尾添加元素、在指定位置插入元素、删除指定位置的元素、获取指定位置的元素、获取链表长度等操作。在算法实现中,通过一个Node类来表示链表中的节点,包含数据域、前驱指针和后继指针。同时,链表的头节点也是一个Node对象,通过头节点来连接链表的首尾,形成双向循环链表
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值