双向链表

双向链表(Double Linked List) 虽然单向链表能够100%解决逻辑关系为“一对一”数据的存储问题,但在解决那些需要大量查找前趋节点的问题是,单向链表无疑是不能用了,因为单向链表适合“从前往后”查找,并不适合“从后往前”查找。如果要提高链表的查找效率,那双向链表(双链表)无疑是首选。双向链表字面上的意思是“双向”的链表,如图1所示。图1 - 双向链表示意图双向指各个节点之间的逻辑关系是双向的,该链表通常只有一个头节点。...... 阅读详情

双向链表概述

双向链表是一种基本的数据结构,有着广泛的应用。本博文实现了双向链表的创建、删除、遍历、替换、插入(从表头插入和从表尾插入)、元素的移动以及其他的一些链表基本操作(判断是否为空、链表拆分、链表的合并、获取第一个元素、获取下一个元素、获取上一个元素等等)。具体的代码实现可以从http://download.youkuaiyun.com/detail/it_pcode/6632905获取。为了便于学习,本文也将代码贴出。并给出了一个简单的例子来使用该双向链表。

双向链表实现

该实现主要是来自于Linux内核对双向链表的实现,其实现精妙无比,与平时我们所使用的双向链表有所不同。请细细品味。

#ifndef LIST_H_
#define LIST_H_

#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>

/*通过父结构体type中的成员member的已知地址ptr,来寻找当前ptr地址所属的父结构体type的地址*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

/*内核预加载内容到RAM,在此不做实现*/
#define prefetch(x) (x)

/*
 * Simple doubly linked list implementation.
 *
 * Some of the internal functions ("__xxx") are useful when
 * manipulating whole lists rather than single entries, as
 * sometimes we already know the next/prev entries and we can
 * generate better code by using them directly rather than
 * using the generic single-entry routines.
 */

struct list_head {
	struct list_head *next, *prev;
};

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

static inline void INIT_LIST_HEAD(struct list_head *list) {
	list->next = list;
	list->prev = list;
}

/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_add(struct list_head *new, struct list_head *prev,
		struct list_head *next) {
	next->prev = new;
	new->next = next;
	new->prev = prev;
	prev->next = new;
}

/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head) {
	__list_add(new, head, head->next);
}

/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head) {
	__list_add(new, head->prev, head);
}

/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_del(struct list_head * prev, struct list_head * next) {
	next->prev = prev;
	prev->next = next;
}

/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 * Note: list_empty() on entry does not return true after this, the entry is
 * in an undefined state.
 */
static inline void list_del(struct list_head *entry) {
	__list_del(entry->prev, entry->next);
	entry->next = NULL;
	entry->prev = NULL;
}

/**
 * list_replace - replace old entry by new one
 * @old : the element to be replaced
 * @new : the new element to insert
 *
 * If @old was empty, it will be overwritten.
 */
static inline void list_replace(struct list_head *old, struct list_head *new) {
	new->next = old->next;
	new->next->prev = new;
	new->prev = old->prev;
	new->prev->next = new;
}

static inline void list_replace_init(struct list_head *old,
		struct list_head *new) {
	list_replace(old, new);
	INIT_LIST_HEAD(old);
}

/**
 * list_del_init - deletes entry from list and reinitialize it.
 * @entry: the element to delete from the list.
 */
static inline void list_del_init(struct list_head *entry) {
	__list_del(entry->prev, entry->next);
	INIT_LIST_HEAD(entry);
}

/**
 * list_move - delete from one list and add as another's head
 * @list: the entry to move
 * @head: the head that will precede our entry
 */
static inline void list_move(struct list_head *list, struct list_head *head) {
	__list_del(list->prev, list->next);
	list_add(list, head);
}

/**
 * list_move_tail - delete from one list and add as another's tail
 * @list: the entry to move
 * @head: the head that will follow our entry
 */
static inline void list_move_tail(struct list_head *list,
		struct list_head *head) {
	__list_del(list->prev, list->next);
	list_add_tail(list, head);
}

/**
 * list_is_last - tests whether @list is the last entry in list @head
 * @list: the entry to test
 * @head: the head of the list
 */
static inline int list_is_last(const struct list_head *list,
		const struct list_head *head) {
	return list->next == head;
}

/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static inline int list_empty(const struct list_head *head) {
	return head->next == head;
}

/**
 * list_empty_careful - tests whether a list is empty and not being modified
 * @head: the list to test
 *
 * Description:
 * tests whether a list is empty _and_ checks that no other CPU might be
 * in the process of modifying either member (next or prev)
 *
 * NOTE: using list_empty_careful() without synchronization
 * can only be safe if the only activity that can happen
 * to the list entry is list_del_init(). Eg. it cannot be used
 * if another CPU could re-list_add() it.
 */
static inline int list_empty_careful(const struct list_head *head) {
	struct list_head *next = head->next;
	return (next == head) && (next == head->prev);
}

/**
 * list_is_singular - tests whether a list has just one entry.
 * @head: the list to test.
 */
static inline int list_is_singular(const struct list_head *head) {
	return !list_empty(head) && (head->next == head->prev);
}

static inline void __list_cut_position(struct list_head *list,
		struct list_head *head, struct list_head *entry) {
	struct list_head *new_first = entry->next;
	list->next = head->next;
	list->next->prev = list;
	list->prev = entry;
	entry->next = list;
	head->next = new_first;
	new_first->prev = head;
}

/**
 * list_cut_position - cut a list into two
 * @list: a new list to add all removed entries
 * @head: a list with entries
 * @entry: an entry within head, could be the head itself
 *	and if so we won't cut the list
 *
 * This helper moves the initial part of @head, up to and
 * including @entry, from @head to @list. You should
 * pass on @entry an element you know is on @head. @list
 * should be an empty list or a list you do not care about
 * losing its data.
 *
 */
static inline void list_cut_position(struct list_head *list,
		struct list_head *head, struct list_head *entry) {
	if (list_empty(head))
		return;
	if (list_is_singular(head) && (head->next != entry && head != entry))
		return;
	if (entry == head)
		INIT_LIST_HEAD(list);
	else
		__list_cut_position(list, head, entry);
}

static inline void __list_splice(const struct list_head *list,
		struct list_head *prev, struct list_head *next) {
	struct list_head *first = list->next;
	struct list_head *last = list->prev;

	first->prev = prev;
	prev->next = first;

	last->next = next;
	next->prev = last;
}

/**
 * list_splice - join two lists, this is designed for stacks
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static inline void list_splice(const struct list_head *list,
		struct list_head *head) {
	if (!list_empty(list))
		__list_splice(list, head, head->next);
}

/**
 * list_splice_tail - join two lists, each list being a queue
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static inline void list_splice_tail(struct list_head *list,
		struct list_head *head) {
	if (!list_empty(list))
		__list_splice(list, head->prev, head);
}

/**
 * list_splice_init - join two lists and reinitialise the emptied list.
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * The list at @list is reinitialised
 */
static inline void list_splice_init(struct list_head *list,
		struct list_head *head) {
	if (!list_empty(list)) {
		__list_splice(list, head, head->next);
		INIT_LIST_HEAD(list);
	}
}

/**
 * list_splice_tail_init - join two lists and reinitialise the emptied list
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * Each of the lists is a queue.
 * The list at @list is reinitialised
 */
static inline void list_splice_tail_init(struct list_head *list,
		struct list_head *head) {
	if (!list_empty(list)) {
		__list_splice(list, head->prev, head);
		INIT_LIST_HEAD(list);
	}
}

/**
 * list_entry - get the struct for this entry
 * @ptr:	the &struct list_head pointer.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

/**
 * list_first_entry - get the first element from a list
 * @ptr:	the list head to take the element from.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_struct within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define list_first_entry(ptr, type, member) \
	list_entry((ptr)->next, type, member)

/**
 * list_for_each	-	iterate over a list
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 */
#define list_for_each(pos, head) \
	for (pos = (head)->next; prefetch(pos->next), pos != (head); \
        	pos = pos->next)

/**
 * __list_for_each	-	iterate over a list
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 *
 * This variant differs from list_for_each() in that it's the
 * simplest possible list iteration code, no prefetching is done.
 * Use this for code that knows the list to be very short (empty
 * or 1 entry) most of the time.
 */
#define __list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)

/**
 * list_for_each_prev	-	iterate over a list backwards
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 */
#define list_for_each_prev(pos, head) \
	for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
        	pos = pos->prev)

/**
 * list_for_each_safe - iterate over a list safe against removal of list entry
 * @pos:	the &struct list_head to use as a loop cursor.
 * @n:		another &struct list_head to use as temporary storage
 * @head:	the head for your list.
 */
#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); \
		pos = n, n = pos->next)

/**
 * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
 * @pos:	the &struct list_head to use as a loop cursor.
 * @n:		another &struct list_head to use as temporary storage
 * @head:	the head for your list.
 */
#define list_for_each_prev_safe(pos, n, head) \
	for (pos = (head)->prev, n = pos->prev; \
	     prefetch(pos->prev), pos != (head); \
	     pos = n, n = pos->prev)

/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member);	\
	     prefetch(pos->member.next), &pos->member != (head); 	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

/**
 * list_for_each_entry_reverse - iterate backwards over list of given type.
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry_reverse(pos, head, member)			\
	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
	     prefetch(pos->member.prev), &pos->member != (head); 	\
	     pos = list_entry(pos->member.prev, typeof(*pos), member))

/**
 * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
 * @pos:	the type * to use as a start point
 * @head:	the head of the list
 * @member:	the name of the list_struct within the struct.
 *
 * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
 */
#define list_prepare_entry(pos, head, member) \
	((pos) ? : list_entry(head, typeof(*pos), member))

/**
 * list_for_each_entry_continue - continue iteration over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 *
 * Continue to iterate over list of given type, continuing after
 * the current position.
 */
#define list_for_each_entry_continue(pos, head, member) 		\
	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\
	     prefetch(pos->member.next), &pos->member != (head);	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

/**
 * list_for_each_entry_continue_reverse - iterate backwards from the given point
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 *
 * Start to iterate over list of given type backwards, continuing after
 * the current position.
 */
#define list_for_each_entry_continue_reverse(pos, head, member)		\
	for (pos = list_entry(pos->member.prev, typeof(*pos), member);	\
	     prefetch(pos->member.prev), &pos->member != (head);	\
	     pos = list_entry(pos->member.prev, typeof(*pos), member))

/**
 * list_for_each_entry_from - iterate over list of given type from the current point
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 *
 * Iterate over list of given type, continuing from current position.
 */
#define list_for_each_entry_from(pos, head, member) 			\
	for (; prefetch(pos->member.next), &pos->member != (head);	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

/**
 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @pos:	the type * to use as a loop cursor.
 * @n:		another type * to use as temporary storage
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry_safe(pos, n, head, member)			\
	for (pos = list_entry((head)->next, typeof(*pos), member),	\
		n = list_entry(pos->member.next, typeof(*pos), member);	\
	     &pos->member != (head); 					\
	     pos = n, n = list_entry(n->member.next, typeof(*n), member))

/**
 * list_for_each_entry_safe_continue
 * @pos:	the type * to use as a loop cursor.
 * @n:		another type * to use as temporary storage
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 *
 * Iterate over list of given type, continuing after current point,
 * safe against removal of list entry.
 */
#define list_for_each_entry_safe_continue(pos, n, head, member) 		\
	for (pos = list_entry(pos->member.next, typeof(*pos), member), 		\
		n = list_entry(pos->member.next, typeof(*pos), member);		\
	     &pos->member != (head);						\
	     pos = n, n = list_entry(n->member.next, typeof(*n), member))

/**
 * list_for_each_entry_safe_from
 * @pos:	the type * to use as a loop cursor.
 * @n:		another type * to use as temporary storage
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 *
 * Iterate over list of given type from current point, safe against
 * removal of list entry.
 */
#define list_for_each_entry_safe_from(pos, n, head, member) 			\
	for (n = list_entry(pos->member.next, typeof(*pos), member);		\
	     &pos->member != (head);						\
	     pos = n, n = list_entry(n->member.next, typeof(*n), member))

/**
 * list_for_each_entry_safe_reverse
 * @pos:	the type * to use as a loop cursor.
 * @n:		another type * to use as temporary storage
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 *
 * Iterate backwards over list of given type, safe against removal
 * of list entry.
 */
#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
		n = list_entry(pos->member.prev, typeof(*pos), member);	\
	     &pos->member != (head); 					\
	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))

#endif /* LIST_H_ */

使用实例

要想使用该实现的双向链表,还需实现自己的查找操作函数。本博文简单实现如下:

struct stu_node {
	struct list_head listhead;
	int age;
};

//红黑树最大节点数目
#define STU_NUM 20

struct stu_node *stu_search(struct list_head *head, int age) {
	struct stu_node *stu = NULL;
	struct list_head *cur;
	list_for_each(cur, head) {
		stu = container_of(cur, struct stu_node, listhead);
		if (stu->age == age) {
			return stu;
		}
	}
	return NULL;
}

测试用例

void testlist() {
	struct list_head head;
	struct stu_node *stu = NULL;
	struct list_head *cur;
	int i = 0;

	INIT_LIST_HEAD(&head);
	if (1 == list_empty(&head)) {
		printf("start, list is empty \n");
	} else {
		printf("list is not empty,return \n");
		return;
	}
	//insert
	printf("start to insert element to the list \n");
	for (i = 0; i < STU_NUM; i = i + 2) {
		stu = malloc(sizeof(struct stu_node));
		stu->age = i + 10;
		list_add(&stu->listhead, &head);
	}

	for (i = 1; i < STU_NUM; i = i + 2) {
		stu = malloc(sizeof(struct stu_node));
		stu->age = i + 10;
		list_add(&stu->listhead, &head);
	}

	//travel
	printf("start to travel the list \n");

	list_for_each(cur, &head) {
		stu = container_of(cur, struct stu_node, listhead);
		printf("age %d \n", stu->age);
	}
	printf("end to travel the list \n");
	//delete
	srand(time(NULL));
	int key = rand() % STU_NUM;
	stu = stu_search(&head, 10 + key);
	if (NULL != stu) {
		printf("age %d is found, next delete it\n", stu->age);
		list_del(&stu->listhead);
		free(stu);
	} else {
		printf("age %d is not found\n", 10 + key);
	}
	stu = stu_search(&head, 10 + key);
	if (NULL != stu) {
		printf("delete age %d failed\n", 10 + key);
	} else {
		printf("delete age %d succeed\n", 10 + key);
	}

	return;
}

在main函数中,仅仅需要调用该测试接口即可。

C++实现单向链表,双向链表,单向环形链表,双向环形链表 本文讲解了链表的四种类型以及数据结构,同时给出了四种链表的C++代码实现。 阅读详情

相关推荐

Agent Skills:AI智能体规模化落地的基础设施

摘要:AgentSkills体系已成为AI智能体落地的核心基础设施,解决了能力扩展和行业知识沉淀两大核心难题。该开放标准通过语义优先、渐进式披露等设计原则,实现了工具调用的标准化和可控化,同时支持轻量化知识沉淀。Skill作为最小能力单元,将传统软件功能、行业知识等封装为可被智能体调用的标准化模块,推动了软件产业从"工具时代"向"智能体时代"的范式变革。目前该体系已被主流Agent工具广泛兼容,形成完整生态,未来将持续演进,进一步降低落地门槛,推动AI原生转型。

技术引领业务创新 1158

list_cut_position-linux链表-5

注解: 对于list_cut_position的用法不能理解,看了实际的例子才知道如何用的。 主要是对于list、entry和head的关系没搞太明白。 list:是一个空链表头,可以是head也可以不是 entry:是head链表的任意一个节点 这样就比较好理解了,加入我有个链表,链表头为head,下面有0-5,共5个节点。 我现在初始化了一个新的链表头list,下面没有节点。 我想把head下面的3-5号节点拿过来。 调用接口传参为: list_cut_position(lis.

u010743406的博客 887

PnP 单目相机位姿估计(二):solvePnP利用二维码求解相机世界坐标

前言 原理简介 输入参数准备 1 objectPoints特征点世界坐标 2 imagePoints特征点在摄像头下的像素点坐标 3cameraMatrixdistCoeffs内参矩阵和畸变矩阵 相机世界坐标的求解 1求世界坐标中的点在相机坐标系下的坐标 2求相机在世界坐标中的坐标 3效果求解相机在世界坐标系下的坐标点 4求解代码1.前言  很多时候,当我们利用单目相机获取图像时,往往只能获取图像中

cocoaqin的博客 5万+

【数据结构--双链表】

文章目录双向链表双向链表的定义双向链表的存储结构双向链表的初始化双向链表的插入双向链表的删除寻找尾结点正向打印反向打印链表节点的读取测试运行截图完整代码闵版代码(略改)总结 双向链表 双向链表的定义 什么是双向链表? 双向链表顾名思义,就是链表由单向的链变成了双向链。双向链表(double linked list)是在单链表的每个结点中再设置一个指向其前驱结点的指针域。 非空带头结点的双向链表 双向链表的存储结构 //双链表节点结构体 typedef struct DoubleLinkNode {

qq_61719813的博客 3916

win11本地部署 DeepSeek-R1 大模型!免费开源,媲美OpenAI-o1能力,断网也能用

是否安装成功,然后直接运行部署deepseek-r1的命令。,出现下面界面即为安装成功。在cmd窗口中先输入。

记录学习编程的日常 2992

S32DS新建工程、编译调试下载、导入工程详细介绍

新建了一个工程之后,下面简单介绍下基本的UI界面。其中最左下角那个是界面,这个界面很重要,是图形化配置界面,然后可以根据这些配置自动生成代码。其中,左边那些配置文件,都是系统帮我们创建好了的,我们其实可以直接用就行。有PE工具的调试,以及Jlink工具的调试。我目前使用到PE工具调试可以查看寄存器值,而Jlink调试查看不了寄存器值。连接了开发板之后,点击Debug就可以吧程序下载到芯片上了,而且可以进入到调试界面。

luobeihai的博客 2万+

C语言函数大全-- l 开头的 Linux 内核函数(链表管理函数)

C语言函数大全-- l 开头的Linux内核函数(链表管理函数)

Huazie 1008

[运营] ext4分区遇到“Structure needs cleaning”问题,怎么解决 - 结构需要清理

摘要:当ext4文件系统出现"Structure needs cleaning"错误时,表明存在元数据损坏。解决方法包括:1)检查系统日志确认错误;2)卸载受影响分区;3)使用fsck.ext4分步骤修复(先测试模式-n,再保守修复-p,严重损坏时用-y强制修复);4)检查修复结果;5)重新挂载。若数据丢失可尝试从lost+found恢复。建议定期备份、避免异常关机并定期检查文件系统,以防硬件故障导致类似问题。修复前务必备份重要数据。

Rudon滨海渔村的博客 1357

双向链表详解

4.newnode有两个指针,它现在是新的尾结点,所以她的next指针指向头结点phead,它的prev指针指向前一个节点也是就phead->prev指向的节点,那原来的尾结点的next指针也要改变,改完指向下一个节点也就是newnode,头结点的prev指针也要改变,现在它指向新的尾结点也就是newnode。4.phead->next指针指向的节点的prev指针改变指向,现在指向newnode,phead指向节点的next指针改变指向,现在指向newnode。newnode的prev指针指向phead。

xpcxpt的博客 2364

1、初探 Tiny Machine Learning

Tiny Machine Learning(TinyML)是机器学习与嵌入式系统的融合领域,致力于在资源受限的微控制器上运行AI模型。本文介绍了机器学习基础、TinyML的核心概念、应用领域(如智能家居、工业物联网、医疗保健)、面临的挑战(如资源限制、模型压缩、数据质量),以及从硬件准备到模型部署的实践步骤。通过利用训练与推理的资源不对称性,可在PC上训练模型并部署至低功耗设备进行实时推理,推动边缘智能的发展。

loss4bartender的博客 121

团体程序设计天梯赛竞赛题--登顶题【L3-039 攀岩】

登顶级 3 道题,每道题 30 分,满分为 90 分。

Pretty Boy Fox的博客 653

什么是双向链表,一篇搞懂双向链表

双向链表,python3

hahaha_1112的博客 1970

【Java基础知识 1】Java入门级概述

优快云最强Java专栏,包含全部Java基础知识点、Java8新特性、Java集合、Java多线程、Java代码实例,理论结合实战,实现Java的轻松学习。

学Java,找哪吒 16万+

双向链表的实现

本文主要介绍了双向链表的结构,概念,相关接口的具体实现方法和链表与顺序表的区别, 并简要介绍了哨兵位的含义与作用。

2303_81060385的博客 1243

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【数据结构】之双向链表、双端(双向)链表、循环(双向)链表

双向链表、双端(双向)链表、循环(双向)链表示意(简)图: 声明:下面各图中,箭头指向的是整个节点,而不是指向节点中的prior或next。 双向链表:只有一个指针指向最开始的结点。 双端(双向)链表:有两个指针分别指向两端的节点。 循环(双向)链表:指向形成一个闭环。 双端(双向)链表的Java实现(简单示例): 注:双向链表、双端(双向)链表、循环(双向)链表的实现思路...

JustryDeng 2211

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

数据结构——双向链表(双向连接的图解、双向链表的创建、操作双向链表)

举个例子: 假设一个文本编辑用链表来存储文本. 每一行用一个String对象存储在链表的一个节点中. 当编辑器用户向下移动光标时, 链表直接操作到下一个节点即可. 但是当用于将光标向上移动呢?- 我们可以轻松的到达下一个节点, 但是回到前一个节点是很难的. 但是, 在实际开发中, 经常会遇到需要回到上一个节点的情况。- 也就是链表相连的过程是单向的. 实现的原理是上一个链表中有一个指向下一个的引用.- 一个节点既有向前连接的引用, 也有一个向后连接的引用.- 既可以从头遍历到尾, 又可以从尾遍历到头。

qq_52301431的博客 4575

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【双向链表】数据结构双向链表的实现

双向链表的实现

m0_56069910的博客 2196

第2章 双向链表

对链表而言,双向均可遍历是最方便的,另外首尾相连循环遍历也可大大增加链表操作的便捷性。因 此,双向循环链表,是在实际运用中是最常见的链表形态。

m0_74916669的博客 945

数组实现双向链表、双向链表的操作

分析: 思路: 遍历:和单链表一样,只是可以向前,也可以向后查找。 添加(默认添加到双向链表的最后) (1).先找到双向链表的最后这个节点(temp) (2).temp.next = newHeroNode (3).newHeroNode.pre = temp; 修改思路和单链表一样 删除 (1).以为是双向链表,因此,我们可以实现自我删除某个节点 (2).直接找到要删除的这个节点,比...

qq_40823802的博客 2011

双向链表和双向循环链表

1.创建一个新结点2.遍历链表找到最后一个结点p3.p结点的后继指针指向新结点,新结点的前驱指针指向p,新结点的后继指针指向NULL;void Insert_tail(DLIST head, DLIST node)\\尾部插入= NULL)void Show(DLIST head)\\打印链表while (p!= NULL)int main()\\运行Show(head);return 0;

weixin_63082397的博客 1015

特殊的链表——双向链表

一、双向链表的概述 1、双向链表的定义:在单链表的每个结点里再增加一个指向其直接前驱的指针域prior,这样链表中就形成了有两个方向不同的链,故称为双向链表。 2、优点:双向链表可以克服单链表查找链表中某结点不方便的缺点。 3、双向循环链表:和单链的循环表类似,双向链表也可以有循环表。让头结点的前驱指针指向链表的最后一个结点,让最后一个结点的后继指针指向头结点。 二、双向链表的特点与算法操作 1、双向链表结构的对称性:设指针P指向某一结点,侧有: p->next->prior=p=p

kxbdys的博客 4419

双向链表的双向遍历

双向链表的双向遍历 双向链表相比较于单向链表的优势之一就是可以快速遍历,对于单向链表只能借助于单个指针逐个遍历,而对于双向链表而言因为每个节点都存在一个前指针和后指针,所以可以借助于两个指针双向遍历,相对于单向链表而言可以节省很多时间。但是对于双向链表的双向遍历需要考虑的因素偏多,代码的步骤需要非常严谨。对于两个指针而言需要考虑节点数是奇数还是偶数,若是奇数,要考虑两指针相遇的情况,只需要输出其中...

A___jay的博客 3890
上一篇: 红黑树实现
下一篇: hash链表
IT_Linux
博客等级 码龄14年 31粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值