双向循环链表和内核链表

news2024/9/22 12:50:22

目录

双向循环链表

结构设计

初始化

 插入

 删除

遍历(顺序/逆序,打印输出)

查找

 主函数

内核链表

 内核链表初始化定义

 内核链表的插入定义

 内核链表的遍历定义

内核链表剔除节点定义

 内核链表如何移动节点定义

内核链表的应用

临时补充:内核链表如何通过内核小结构体访问大结构体中的元素


双向循环链表

结构设计

可以双向遍历(前后遍历),首尾相连,一般在链表的初始化中设置两个指针域,一个指向前驱元素,一个指向后继元素。

struct node
{
    // 以整型数据为例
    int data; 

    // 指向相邻的节点的双向指针
    struct node * prev;
    struct node * next;
} ;

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

其他的部分和单链表一样

初始化

所谓初始化,就是构建一条不含有效节点的空链表。
以带头结点的双向循环链表为例,初始化后,其状态如下图所示:

双循环链表中链表的初始化就是让头节点的前驱和后继指针都指向自身

初始化代码有如下两种形式,功能相同

第一种是返回一个初始化头节点指针的指针函数

struct node *init_node()
{
    struct node *xnew = malloc(sizeof(struct node));

    if(xnew == NULL)
    {
        printf("初始化失败");
        return NULL;
    }
    xnew->next = xnew;
    xnew->prev = xnew;

    return xnew;
}

第二种是通过给函数传入未初始化的头节点的二级指针修改头节点 ,可以看看两种初始化方式有什么区别

void init_node(struct node **head)
{
if(((*head)=(struct node *)malloc(sizeof(struct node))) == NULL)
    {
		perror("malloc faild");
		exit(1);
	}

    (*head)->prev = (*head)->next = *head;
}

 初始化一个带参数的节点,一般在链表的插入中使用

struct node *create_node(int val)
{
    struct node *xnew = malloc(sizeof(struct node));

    if(xnew == NULL)
    {
        printf("初始化失败");
        return NULL;
    }

    xnew->data = val;
    xnew->next = xnew;
    xnew->prev = xnew;

    return xnew;
}
 插入

插入操作也分为两种,一种是在头部插入,一种是在尾部插入,分别为头插法和尾插法,两种插入方式的遍历顺序相反。

头插法

void insert_top(struct node *head, struct node *xnew)
{
    xnew->next = head->next;
    xnew->prev = head;

    head->next->prev = xnew;
    head->next = xnew;
}

尾插法 

void insert_tail(struct node *head, struct node *xnew)
{
    xnew->next = head;
    xnew->prev = head->prev;

    head->prev->next = xnew;
    head->prev = xnew;
}

链表的插入操作秉承着先链接再断开的思想,即先将要插入的节点和前后节点相连,再断开前后节点

 删除

在删除操作中要定义一个临时的指针指向要删除的节点的地址,然后pos指针要回退到要删除元素的前一个元素,继续遍历,然后释放临时指针tmp,这是为了pos指针能遍历完链表中的每一个元素,解决了链表中有重复元素的问题

void del_node(struct node *head, int val)
{
    struct node *pos = head->next;

    while(pos != head)
    {
        if(pos->data == val)
        {
            pos->prev->next = pos->next;
            pos->next->prev = pos->prev;

            struct node *tmp = pos;

            pos = pos->prev;

            tmp->next = NULL;
            tmp->prev = NULL;

            free(tmp);
        }
        pos = pos->next;
    }
}
遍历(顺序/逆序,打印输出)
void show(struct node *head)
{
    struct node *pos = head->next;
    
    while(pos != head)
    {
        printf("%d\t", pos->data);
        
        pos = pos->next;
    } 
    printf("\n");
}

查找

顺序遍历链表,找到要返回的元素的地址,直接返回该元素的指针

struct node *find(struct node *head, int val)
{
    struct node *pos = head->next;

    while(pos != head)
    {
        if(pos->data == val)
        {
            return pos;
        }
        pos = pos->next;
    }
}

 

 主函数
int main()
{
    //第一种初始化方式
    struct node *head;
    init_node(head);

    //第二种初始化方式
    strutc node *head2 = init_node();

    //插入方式,先初始化要插入的节点,再插入
    strutc node *xnew1 = create_node(10);
    strutc node *xnew2 = create_node(20);
    strutc node *xnew3 = create_node(30);
    strutc node *xnew4 = create_node(40);
    strutc node *xnew5 = create_node(50);

    //头插法插入
    insert_top(head, xnew1);
    insert_top(head, xnew2);
    insert_top(head, xnew3);
    insert_top(head, xnew4);
    insert_top(head, xnew5);

    show(head);

    //尾插法插入
    insert_tail(head, xnew1);
    insert_tail(head, xnew2);
    insert_tail(head, xnew3);
    insert_tail(head, xnew4);
    insert_tail(head, xnew5);

    show(head);

    //删除
    del_node(head, 20);

    show(head);

    //查找
    struct node *pos = find(head, 50);
    printf("%d", pos->data);
    

内核链表

在我们之前的链表设计中,我们发现,我们所设计的节点元素都是以整形变量为例的,那有没有一种方法,能让我们构建一个链表中的元素可以是任意值的链表,包括什么浮点型,double型,长整型之类的,仅仅依靠指针将他们连在一起?

内核链表其实就是一个双向循环链表

为了解决这个办法,我们把链表抽象为一个接口,这个链表接口中除了前驱和后继节点以外什么都不包括,数据的类型全凭我们定义,在Linux内核中,已经有人帮我们定义好了这种链表的接口,这就是内核链表,其在驱动和Linux开发中具有重要作用

下面介绍一下内核链表的常用各个接口,可移植的完整内核链表接口放在附录中

内核链表中用了大量宏定义替换函数的使用,大大提高了性能和简化了代码

 内核链表初始化定义
#define INIT_LIST_HEAD(ptr) do { \
	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
 内核链表的插入定义
// 内部函数
// 将节点new插入到prev与next之间
// 注意,所有的指针都是标准节点指针,与用户数据无关
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;
}

// 将新节点new插入到链表head的首部
// 即:插入到head的后面
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}

// 将新节点new插入到链表head的尾部
// 即:插入到head的前面
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}
 内核链表的遍历定义
// 向后遍历链表每一个节点
// 注意:
// 遍历过程不可删除节点
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next)


// 安全版:
// 向后遍历链表的每一个节点
// 支持边遍历,边删除节点
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)


// 向后遍历链表的每一个节点并直接获得用户节点指针
// 注意:
// 遍历过程不可删除节点
#define list_for_each_entry(pos, head, member)                \
for (pos = list_entry((head)->next, typeof(*pos), member);    \
&pos->member != (head);                     \
pos = list_entry(pos->member.next, typeof(*pos), member))


// 安全版:
// 向后遍历链表的每一个节点并直接获得用户节点指针
// 支持边遍历,边删除节点
#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))


// 向前遍历链表的每一个节点
// 注意:
// 遍历过程不可删除节点
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); \
pos = pos->prev)
内核链表剔除节点定义
// 内部函数
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
	next->prev = prev;
	prev->next = next;
}

// 将指定节点 entry 从链表结构中剔除
// 并将其前后指针置空
static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = (void *) 0;
	entry->prev = (void *) 0;
}

// 将指定节点 entry 从链表结构中剔除
// 并将其前后指针指向自身
static inline void list_del_init(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	INIT_LIST_HEAD(entry);
}
 内核链表如何移动节点定义
// 将节点list,移动到指定位置head的后面
static inline void list_move(struct list_head *list,
                             struct list_head *head)
{
	__list_del(list->prev, list->next);
	list_add(list, head);
}

// 将节点list,移动到指定位置head的前面
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);
}

内核链表的应用

设计一个内核链表,将链表中的双数和单数分开,双数放在链表得到末尾

思路,利用内核链表的移动节点定义,顺序遍历双链表,将双数筛选出来,再用移动节点定义将双数放在链表的末尾

设计一个大结构体,同时包含数据域和内核指针域

struct node
{
    int data;              // 数据域
    struct list_head list; // 地址域
};

初始化

struct node *create_node(int data)
{
    struct node *xnew = malloc(sizeof(struct node));
    if (xnew == NULL)
    {
        printf("创建节点失败\n");
        return NULL;
    }
    // 初始化节点
    xnew->data = data; // 初始化数据域
    INIT_LIST_HEAD(&xnew->list);
    
    return  xnew;
}

 尾插法插入节点(其实头插法也一样,调用相应的内核函数即可,尾插法只是想要一个正的顺序而已)

void insert_node(struct list_head *head, int data)
{
    // 1.新建节点
    struct node *xnew = create_node(data);

    // 2.插入节点
    list_add_tail(&xnew->list, head);
}
临时补充:内核链表如何通过内核小结构体访问大结构体中的元素

方法一:内存对齐、

即为在定义结构体时,将内核指针域放在结构体中的第一个元素位置,这样内核指针域(小结构体)的地址跟用户定义大结构体地址相等,如下代码

struct node 
{
    struct list_head list;
    int a;
    flaot b;
    double c;
}

方法二:指针数据类型强制转换相减(先得到小结构体的地址,然后减去偏移量(用户定义数据所占用内存的大小)即可得到大结构体的地址)

其在内核中的宏定义为

/**
* 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) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

ptr:小结构体的地址

type:大结构体的类型

member:小结构体在大结构体中的名称

即以0地址为基准,用户定义变量内存字节大小为偏移量,然后小结构体的真实地址减去这个相对偏移地址

下面的代码中都使用内核定义的访问结构体宏定义list_entry()

删除节点

void del_node(struct list_head *head, int del_data)
{
    struct list_head *pos = NULL;
    list_for_each(pos, head)
    {
        // 获取大结构体地址
        struct node *p = list_entry(pos, struct node, list);

        // 判断是否为删除的节点
        if (p->data == del_data)
        {
            list_del(pos);

            free(p);

            return;
        }
    }
}

 查找节点

struct node *find_node(struct list_head *head, int f_data)
{
    struct list_head *pos = NULL;
    list_for_each(pos, head)
    {
        // 获取大结构体地址
        struct node *p = list_entry(pos, struct node, list);

        if (p->data == f_data)
        {
            return p;
        }
    }

    return NULL;
}

修改节点

void change_node(struct list_head *head, int old_data, int new_data)
{
    // 1.查找节点
    struct node *p = find_node(head, old_data);
    if (p == NULL)
    {
        printf("无此数据\n");
    }
    else
    {
        p->data = new_data;
    }
}

 奇偶排序

void rearrange(linklist head)
{
	struct list_head *pos, *odd=(&head->list)->prev;
	linklist p;

	list_for_each_prev(pos, &head->list)
	{
		p = list_entry(pos, listnode, list);

		if(p->data % 2 == 0)
		{
			list_move_tail(pos, &head->list);
			pos = odd;
		}
		else
			odd = pos;
	}
}

 主函数

#include <stdio.h>
#include <stdlib.h>
#include "kernel_list.h"





int main()
{
    // 1.创建头节点
    struct list_head *head = malloc(sizeof(struct list_head));

    INIT_LIST_HEAD(head);

    for(int i = 1; i < 10; i++)
    {
        insert_node(head, i);
    }

    show(head);

    rearrange(*head);

    show(head);
}

  //搜索,删除,遍历,插入等功能可自行测试

附录:

移植的内核链表头文件

#ifndef __DLIST_H
#define __DLIST_H

/* This file is from Linux Kernel (include/linux/list.h)
* and modified by simply removing hardware prefetching of list items.
* Here by copyright, credits attributed to wherever they belong.
* Kulesh Shanmugasundaram (kulesh [squiggly] isis.poly.edu)
*/

/*
* 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.
*/
/**
 * container_of - cast a member of a structure out to the containing structure
 *
 * @ptr:	the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:	the name of the member within the struct.
 *
 */
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#define container_of(ptr, type, member) ({			\
        const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
        (type *)( (char *)__mptr - offsetof(type,member) );})
/*
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
#define LIST_POISON1  ((void *) 0x00100100)
#define LIST_POISON2  ((void *) 0x00200)

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

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

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

// 宏定义语法规定只能有一条语句
// 如果需要多条语句,那就必须将多条语句放入一个do{}while(0)中使之成为一条复合语句
#define INIT_LIST_HEAD(ptr) \
    do { \
    (ptr)->next = (ptr); \
    (ptr)->prev = (ptr); \
} while (0)

/*
* 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 = (void *) 0;
	entry->prev = (void *) 0;
}

/**
* 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_empty – tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(struct list_head *head)
{
	return head->next == head;
}

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

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

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

/**
* list_splice – join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, 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);
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) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

/**
* list_for_each    -    iterate over a list
* @pos:    the &struct list_head to use as a loop counter.
* @head:    the head for your list.
*/
#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 counter.
* @head:    the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->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 counter.
* @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_entry    -    iterate over list of given type
* @pos:    the type * to use as a loop counter.
* @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);    \
&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 counter.
* @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))

#endif

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

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

相关文章

身在职场,不得不提防的几个问题,能让少走许多弯路

职场路本就崎岖&#xff0c;如果再走了弯路&#xff0c;脚下的路将会更漫长且难走。 谁不想一帆风顺&#xff0c;可谁又能一帆风顺&#xff1f;不是人心险恶&#xff0c;而是立场本就不同&#xff0c;为了各自的利益考虑无可厚非。 你可以说凭借能力获取利益&#xff0c;为什…

CVE-2023-37569~文件上传【春秋云境靶场渗透】

# 今天我们拿下CVE-2023-37569这个文件上传漏洞# 经过简单账号密码猜测 账号&#xff1a;admin 密码&#xff1a;password# 找到了文件上传的地方# 我们直接给它上传一句话木马并发现上传成功# 上传好木马后&#xff0c;右键上传的木马打开发现上传木马页面# 直接使用蚁剑进行连…

Linux5:Shell编程——函数、重定向

目录 前言 一、函数 1.函数结构 2.函数实例 3.函数传参 二、重定向 1.输出重定向 2.输入重定向 3.同时使用 4.重定向深入了解 5.垃圾桶 总结 前言 Shell编程将会在本章完结 一、函数 1.函数结构 #!/bin/sh # 函数function fun1() {echo "this is a funtion&q…

【有手就行】:从无到有搭建后端SpringBoot项目

前言 想静下心来写点东西&#xff0c;但是确实想不到该写点啥&#xff0c;可能是少了点感觉吧 &#x1f622;。前面刚整理了下前端VUE&#xff0c;就想了下把后端也一起整理下吧&#xff0c;免得换电脑了安装环境又要弄半天&#xff0c;那就开搞吧 首先 准备环境 1.安装IDEA…

云计算实训21——mysql-8.0.33-linux-glibc安装及使用

一、mysql-8.0.33-linux-glibc安装 安装步骤 1.解压 tar -xvf mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz 2.清空其他环境 rm -rf /etc/my.cnf 3.安装依赖库 yum list installed | grep libaio 4.创建用户 useradd -r -s /sbin/nologin mysql 查看 id mysql 5.创建mysql-fi…

PXE批量网络装机(超详细实验教程)教会你自动化批量安装linux 系统 红帽7

1.创建自动化安装服务器 1.1. 搭建本地厂库 写入rpm.re文件内容 [rhel7]namerhel7baseurlfile:///rhel7gpgcheck0 Yum makecache 测试是否挂载成功 1.2.关闭虚拟机的本地DHCP 1.3下载必要软件 下载图形化脚本自动生成工具方便编写脚本 下载dhcp分配ip httpd 搭建网页 …

数据排序之旅

1、排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序…

vue3 + i18n 实现国际化并动态切换语言

安装 npm install vue-i18n// index.ts import { createI18n } from vue-i18n // 语言包 import ch from ./ch import en from ./enconst lang localStorage.getItem(localeLangD) || ch if (!localStorage.getItem(localeLangD)) {localStorage.setItem(localeLangD, lang) …

linux文本命令:文本处理工具awk详解

目录 一、概述 二、基本语法 1、awk 命令的基本语法 2、常用选项 3、获取帮助 三、工作原理 四、 功能特点 五、分割字段 六、 示例 1. 打印所有行 2. 计算总和 3. 过滤特定模式 4. 使用多个模式 5. 复杂的脚本 6. 自定义分隔符 7. 打印指定列 8. 使用 BEGIN …

微信小程序教程011-1:京西购物商城实战

文章目录 1、起步1.1 uni-app简介1.2 开发工具1.2.1 下载HBuilderX1.2.2 安装HBuilderX1.2.3 安装scss/sass编译1.2.4 快捷键方案切换1.3 创建uni-app项目1.4 目录结构1.5 把项目运行到微信开发者工具1.6 使用Git管理项目1.6.1 本地管理1.6.2 把项目托管到码云1、起步 1.1 uni…

【Unity】3D功能开发入门系列(五)

Unity3D功能开发入门系列&#xff08;五&#xff09; 一、预制体&#xff08;一&#xff09;预制体&#xff08;二&#xff09;预制体的创建&#xff08;三&#xff09;预制体实例&#xff08;四&#xff09;预制体的编辑 二、动态创建实例&#xff08;一&#xff09;动态创建实…

2024/8/4 汇川变频器低压产品分类选型

VF就是通过电压、频率控制 矢量就是通过开环&#xff08;svc&#xff09;和闭环&#xff08;fvc&#xff09; MD310、MD200 开环&#xff0c;不支持闭环&#xff0c;无法接编码器 290 、200s、280、都是VF控制

有哪些供应链管理方法?详解四种常用的供应链管理方法!

在当今复杂多变的商业环境中&#xff0c;供应链管理已成为企业获取竞争优势的关键。有效的供应链策略不仅能提升企业的响应速度和市场适应性&#xff0c;还能显著降低成本、提高效率。本文将深入探讨几种主流的供应链管理方法&#xff0c;包括快速反应、有效客户反应、基于活动…

LeetCode 0572.另一棵树的子树:深搜+广搜(n^2做法就能过,也有复杂度耕地的算法)

【LetMeFly】572.另一棵树的子树&#xff1a;深搜广搜&#xff08;n^2做法就能过&#xff0c;也有复杂度耕地的算法&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/subtree-of-another-tree/ 给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 s…

DEBUG:sw模板不对

问题 sw自带模板不合适 解决 工具 选项 文件位置 自己新建一个文件夹 放入模板 &#xff08;三维 二维各一个 一般就是统一标准 可以自己新建个模板&#xff09;

深度学习笔记(神经网络+VGG+ResNet)

深度学习 主要参考博客常用英语单词 概念应用神经网络基础神经网络基本结构 超参数超参数是什么常用超参数超参数搜索过程常用超参数调优办法&#xff08;通过问题复杂度和计算资源选择&#xff09; 激活函数介绍为什么要使用激活函数推荐博客 sigmoid激活函数&#xff08;使用…

【教程-时间序列预测】PyTorch 时间序列预测入门

文章目录 from博客: https://zhajiman.github.io/post/pytorch_time_series_tutorial/#%E9%AB%98%E7%BA%A7%E6%96%B9%E6%B3%95%E8%87%AA%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B 数据集产生 窗口 也是难点&#xff01;

工作中,如何有效解决“冲突”?不回避,不退让才是最佳方式

职场里每个人都在争取自己的利益&#xff0c;由于立场的不同&#xff0c;“冲突”不可避免。区别在于有些隐藏在暗处&#xff0c;有些摆在了台面上。 隐藏在“暗处”的冲突&#xff0c;表面上一团和气&#xff0c;实则在暗自较劲&#xff0c;甚至会有下三滥的手段&#xff1b;…

常见API(二)

API 应用程序编程接口&#xff0c;提高编程效率。本次学习了Object类&#xff0c;Objects工具类&#xff0c;包装类&#xff0c;StringBuilder&#xff0c;StringBuffer&#xff0c;和StringJoiner。 目录 1.Object 常见方法&#xff1a; 2.Objects 常见方法&#xff1a; 3…

AcWing开放地址法和拉链法

开放地址法&#xff0c;把h数组全部设置为3f&#xff0c;然后设定null为0x3f3f3f3f&#xff0c;find函数设定返回值t&#xff0c;如果h[t]null,那么x在h中不存在&#xff0c;否则为存在 #include<iostream> #include<cstring> #include<string> #define LEN…