【数据结构】链表(1):单向链表和单向循环链表

news2025/1/6 13:36:03

链表

链表是一种经典的数据结构,它通过节点的指针将数据元素有序地链接在一起,在链表中,每个节点存储数据以及指向其他节点的指针(或引用)。链表具有动态性和灵活性的特点,适用于频繁插入、删除操作的场景。

定义

概念
  1. 将线性表 L = (a0,a1,...,an-1) 中各元素分布在存储器的不同存储块,称为结点。
    通过指针或地址建立起它们之间的联系,所得到的存储结构就是链表。
  2. 链表都有一个头结点(一般不保存数据,不做遍历),是链表的入口。
  3. 链表呈现一对一关系,且有一个前驱结点和一个后继结点。
  4. 每一个结点都往堆申请了地址,由多个堆组成。
分类
  1. 单向链表(重点)
  2. 单向循环链表
  3. 双向链表
  4. 双向循环链表(重点)
  5. 内核链表(重点)

我们这里将链表分成三篇文章来写,分别是 1:单向链表(重点)和单向循环链表;2:双向链表和双向循环链表(重点);3:内核链表(重点)。因为代码片如若过多可能会导致思维混乱,所以分开来写,将单向链表和循环链表放在一篇文章之中是因为二者的理念相同,只是首位相连接,代码逻辑接近,方便理解和对比。

链表的优缺点(想要具体了解链表和顺序表之间的区别详细,请查阅这篇文章:<链接:【数据结构】顺序表和链表优劣的对比分析>)

  • 优点:插入和删除非常方便。
  • 缺点:查找和替换比较麻烦。
  • 操作:对链表中数据的增删改查 —> 大原则:先连后断
单向链表(Singly Linked List)

定义:

  • 每个节点包含两部分:
    • 数据域(存储数据)。
    • 指针域(存储指向下一个节点的指针)。
  • 只有一个方向,从头节点开始依次访问每个节点。

特点:

  • 插入和删除操作高效,不需要移动其他节点。
  • 无法直接访问某个特定位置的元素,需要从头节点开始遍历。
#define datatype int
typedef struct link{
  datatype data;           //数据域  
  struct link *next;  //指向后继结点的指针域
}link_t;

// 缺点:只能从头结点一直往后走

// 头插:头结点(最前面的结点)后面插入
// 尾插:尾结点(最后面的结点)后面插入
1> 初始化link_init
link_t *link_init(void)  //造头结点
{
    //1>向堆申请
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if(NULL == p)
    {
        perror("malloc");
        return NULL;
    }
    //将指针赋值 ,为了安全指向NULL
    p->next = NULL;
    return p;
}
2> 创建结点create_node(static)
static link_t *create_node(datatype d)
{
    //1>向堆空间申请
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if(NULL == p)
    {
        perror("malloc");
        return NULL;
    }
    //2>赋值
    p->data = d;
    p->next = NULL;
    return p;
}

在这里插入图片描述

3> 插入函数insert_behind(static)
//将一个结点(a)插到另一个结点(b)的后面
static void insert_behind(link_t *a,link_t *b)
{
   //遵循先连后断 
   a->next = b->next;  //避免b指向的地址丢失
   b->next = a;
}
4> 头插函数insert_head
void insert_head(link_t *p,datatype d)
{
    //利用传进来的数据,调用创建结点函数
    link_t *node = create_node(d);
    //将创建出来的node结点插到头结点后面
    insert_behind(node,p);  
}

在这里插入图片描述

5> 遍历展示display
void display(link_t *head)
{
    //遍历整个链表
    while(head->next != NULL)
    {
        //将head指针变量往右移
        head = head->next;
        printf("%d ",head->data); 
    } 
    printf("\n");
}
6> 尾插函数insert_tail
//每插入一个结点进来,将其插入到尾结点后面
void insert_tail(link_t *p,datatype data)
{
    link_t *node = create_node(data);
    if(NULL == node)
        return;
   //找到尾结点 
    while(p->next != NULL)
    {
        p = p->next;
    }
    insert_behind(node,p);  
}

在这里插入图片描述

7> 删除函数link_del
void link_del(link_t *p,datatype d)
{
    link_t *node = NULL;
    //遍历
    while(p->next != NULL)
    {
        //进行数据对比
        if(p->next->data == d) //从头结点后第一个有数据的结点开始判断
        {
           //先保存要删除结点的地址
            node = p->next;
           // 将p的next指向node的next
            p->next = node->next;
           //为了安全,在释放前,令它的指针域指向NULL
            node->next = NULL;
            //释放
            free(node);
            continue;
        }
        //往后继续遍历,找相同数据的结点
       	p = p->next; 
    }
    
}

在这里插入图片描述

8> 修改函数link_replace
void link_replace(link_t *p,int old,int new)
{
    //遍历找到旧数据所在的地址
    while(p->next != NULL)
    {
        if(p->next->data == old)
        {
            p->next->data = new;
            continue; 
        } 
    	p = p->next;
    }
}
完整代码(共三个文件:头文件、链表函数和主函数)

头文件: link.h

#ifndef __LINK_H__
#define __LINK_H__

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

#define datatype int

typedef struct link{
  datatype data;           //数据域  
  struct link *next;  //指向后继结点的指针域
}link_t;

extern link_t *link_init(void);
extern void insert_head(link_t *p,datatype d);
extern void display(link_t *head);
extern void insert_tail(link_t *p,datatype data);
extern void link_del(link_t *p,datatype d);
extern void link_replace(link_t *p,int old,int new);

#endif

链表函数:link.c

#include "link.h"
//初始化
link_t *link_init(void)  //造头结点
{
    //1>向堆申请
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if(NULL == p)
    {
        perror("malloc");
        return NULL;
    }
    //将指针赋值 ,为了安全指向NULL
    p->next = NULL;
    return p;
}

//创建结点
static link_t *create_node(datatype d)
{
    //1>向堆空间申请
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if(NULL == p)
    {
        perror("malloc");
        return NULL;
    }
    //2>赋值
    p->data = d;
    p->next = NULL;
    return p;
}

//插入函数insert_behind
//将一个结点(a)插到另一个结点(b)的后面
static void insert_behind(link_t *a,link_t *b)
{
   //遵循先连后断 
   a->next = b->next;  //避免b指向的地址丢失
   b->next = a;
}

//头插函数insert_head
void insert_head(link_t *p,datatype d)
{
    //利用传进来的数据,调用创建结点函数
    link_t *node = create_node(d);
    //将创建出来的node结点插到头结点后面
    insert_behind(node,p); 
}

//遍历操作
void display(link_t *head)
{
    //遍历整个链表
    while(head->next != NULL)
    {
        //将head指针变量往右移
        head = head->next;
        printf("%d ",head->data); 
    } 
    printf("\n");
}

//每插入一个结点进来,将其插入到尾结点后面
void insert_tail(link_t *p,datatype data)
{
    link_t *node = create_node(data);
    if(NULL == node)
        return;
   //找到尾结点 
    while(p->next != NULL)
    {
        p = p->next;
    }
    insert_behind(node,p);  
}

//删除
void link_del(link_t *p,datatype d)
{
    link_t *node = NULL;
    //遍历
    while(p->next != NULL)
    {
        //进行数据对比
        if(p->next->data == d) //从头结点后第一个有数据的结点开始判断
        {
           //先保存要删除结点的地址
            node = p->next;
           // 将p的next指向node的next
            p->next = node->next;
           //为了安全,在释放前,令它的指针域指向NULL
            node->next = NULL;
            //释放
            free(node);
            continue;
        }
        //往后继续遍历,找相同数据的结点
       	p = p->next; 
    }
}

//替换
void link_replace(link_t *p,int old,int new)
{
    //遍历找到旧数据所在的地址
    while(p->next != NULL)
    {
        if(p->next->data == old)
        {
            p->next->data = new;
            continue; 
        } 
    	p = p->next;
    }
}

主函数:main.c

#include "link.h"

int main(void)
{
    link_t *head = link_init();
    if(NULL == head)
        return -1;
    printf("%p\n",head);
    int data,ret;
    while(1)
    {
        printf("请开始头插\n");
        ret = scanf("%d",&data);
        if(ret == 0)
            break;
        insert_head(head,data); 
        display(head);
    }
    getchar();
    while(1)
    {
        printf("请开始尾插\n");
        ret = scanf("%d",&data);
        if(ret == 0)
            break;
        insert_tail(head,data); 
        display(head);
    }
    getchar();
    while(1)
    {
        printf("请开始删除\n");
        ret = scanf("%d",&data);
        if(ret == 0)
            break;
        link_del(head,data); 
        display(head);
    }
    getchar();
    int old,new;
    while(1)
    {
        printf("请开始替换\n");
        ret = scanf("%d%d",&old,&new);
        if(ret == 0)
            break;
        link_replace(head,old,new); 
        display(head);
    }
    return 0;
}
单向循环链表(Singly Circular Linked List)

定义:

  • 单向链表的变体,最后一个节点的指针指向头节点,形成一个环。
  • 从链表的任何节点出发,都可以遍历整个链表。

特点:

  • 无需额外存储头尾信息,适合循环任务。
  • 需要注意防止死循环。
#define datatype int
typedef struct link{
  datatype data;           //数据域  
  struct link *next;      //指向后继结点的指针域
}link_t;

// 头插:头结点(最前面的结点)后面插入
// 尾插:尾结点(最后面的结点)后面插入
1> 初始化link_init
link_t *link_init(void)  //造头结点
{
    //1>向堆申请
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if(NULL == p)
    {
        perror("malloc");
        return NULL;
    }
    //将指针赋值 ,为了安全指向NULL  //修改处
    p->next = p; //自己指向自己
    return p;
}
2> 创建结点create_node
static link_t *create_node(datatype d)
{
    //1>向堆空间申请
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if(NULL == p)
    {
        perror("malloc");
        return NULL;
    }
    //2>赋值  //修改处
    p->data = d;
    p->next = p;   //指向自己
    return p;
}
3> 插入函数insert_behind(static)
//将一个结点(a)插到另一个结点(b)的后面
static void insert_behind(link_t *a,link_t *b)
{
   //遵循先连后断 
   a->next = b->next;  //避免b指向的地址丢失
   b->next = a;
}
4> 头插函数insert_head
void insert_head(link_t *p,datatype d)
{
    //利用传进来的数据,调用创建结点函数
    link_t *node = create_node(d);
    //将创建出来的node结点插到头结点后面
    insert_behind(node,p); 
}
5> 遍历展示display
void display(link_t *head)
{
    //修改处
    link_t *p = head;
    //遍历整个链表  //修改处
    while(head->next != p)  //最后一个结点指向的是头结点
    {
        //将head指针变量往右移
        head = head->next;
        printf("%d ",head->data); 
    } 
    printf("\n");
}
6> 尾插函数insert_tail
//每插入一个结点进来,将其插入到尾结点后面
void insert_tail(link_t *p,datatype data)
{
    //修改处
    link_t *head = p; 
    link_t *node = create_node(data);
    if(NULL == node)
        return;
   //找到尾结点  //修改处
    while(p->next != head)  //尾结点指向头结点
    {
        p = p->next;
    }
    insert_behind(node,p);  
}
7> 删除函数link_del
void link_del(link_t *p,datatype d)
{
    //修改处,保存头结点的地址
    link_t *head = p;
    link_t *node = NULL;
    //遍历   //修改处
    while(p->next != head)
    {
        //进行数据对比
        if(p->next->data == d) //从头结点后第一个有数据的结点开始判断
        {
           //先保存要删除结点的地址
            node = p->next;
           // 将p的next指向node的next
            p->next = node->next;
           //为了安全,在释放前,令它的指针域指向自己  //修改处
            node->next = node;
            //释放
            free(node);
            continue;
        }
        //往后继续遍历,找相同数据的结点
       	p = p->next; 
    }
    
}
8> 修改函数link_replace
void link_replace(link_t *p,int old,int new)
{
    //修改处,保存头结点地址
    link_t *head = p;
    //遍历找到旧数据所在的地址  修改处
    while(p->next != head)
    {
        if(p->next->data == old)
        {
            p->next->data = new;
            continue; 
        } 
    	p = p->next;
    }
}
完整代码(共三个文件:头文件、链表函数和主函数)

头文件: cyclelink.h

#ifndef __CYCLELINK_H__ 
#define __CYCLELINK_H__

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

#define datatype int
typedef struct link{
  datatype data;           //数据域  
  struct link *next;  //指向后继结点的指针域
}link_t;

extern link_t *link_init(void);
extern void insert_head(link_t *p,datatype d);
extern void display(link_t *head);
extern void insert_tail(link_t *p,datatype data);
extern void link_del(link_t *p,datatype d);
extern void link_replace(link_t *p,int old,int new);

#endif

链表函数: cyclelink.c

#include "cyclelink.h"

link_t *link_init(void)  //造头结点
{
    //1>向堆申请
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if(NULL == p)
    {
        perror("malloc");
        return NULL;
    }
    //将指针赋值 ,为了安全指向NULL  //修改处**
    p->next = p; //自己指向自己
    return p;
}

static link_t *create_node(datatype d)
{
    //1>向堆空间申请
    link_t *p = (link_t *)malloc(sizeof(link_t));
    if(NULL == p)
    {
        perror("malloc");
        return NULL;
    }
    //2>赋值  //修改处
    p->data = d;
    p->next = p;   //指向自己
    return p;
}

//将一个结点(a)插到另一个结点(b)的后面
static void insert_behind(link_t *a,link_t *b)
{
   //遵循先连后断 
   a->next = b->next;  //避免b指向的地址丢失
   b->next = a;
}

void insert_head(link_t *p,datatype d)
{
    //利用传进来的数据,调用创建结点函数
    link_t *node = create_node(d);
    //将创建出来的node结点插到头结点后面
    insert_behind(node,p); 
}

void display(link_t *head)
{
    //修改处
    link_t *p = head;
    //遍历整个链表  //修改处
    while(head->next != p)  //最后一个结点指向的是头结点
    {
        //将head指针变量往右移
        head = head->next;
        printf("%d ",head->data); 
    } 
    printf("\n");
}

//每插入一个结点进来,将其插入到尾结点后面
void insert_tail(link_t *p,datatype data)
{
    //修改处
    link_t *head = p;
    link_t *node = create_node(data);
    if(NULL == node)
        return;
   //找到尾结点  //修改处
    while(p->next != head)  //尾结点指向头结点
    {
        p = p->next;
    }
    insert_behind(node,p);  
}

void link_del(link_t *p,datatype d)
{
    //修改处,保存头结点的地址
    link_t *head = p;
    link_t *node = NULL;
    //遍历   //修改处
    while(p->next != head)
    {
        //进行数据对比
        if(p->next->data == d) //从头结点后第一个有数据的结点开始判断
        {
           //先保存要删除结点的地址
            node = p->next;
           // 将p的next指向node的next
            p->next = node->next;
           //为了安全,在释放前,令它的指针域指向自己  //修改处
            node->next = node;
            //释放
            free(node);
            continue;
        }
        //往后继续遍历,找相同数据的结点
       	p = p->next; 
    }
    
}

void link_replace(link_t *p,int old,int new)
{
    //修改处,保存头结点地址
    link_t *head = p;
    //遍历找到旧数据所在的地址  修改处
    while(p->next != head)
    {
        if(p->next->data == old)
        {
            p->next->data = new;
            continue; 
        } 
    	p = p->next;
    }
}

主函数:main.c

#include "cyclelink.h"

int main(void)
{
    link_t *head = link_init();
    if(NULL == head)
        return -1;
    printf("%p\n",head);
    int data,ret;
    while(1)
    {
        printf("请开始头插\n");
        ret = scanf("%d",&data);
        if(ret == 0)
            break;
        insert_head(head,data); 
        display(head);
    }
    getchar();
    while(1)
    {
        printf("请开始尾插\n");
        ret = scanf("%d",&data);
        if(ret == 0)
            break;
        insert_tail(head,data); 
        display(head);
    }
    getchar();
    while(1)
    {
        printf("请开始删除\n");
        ret = scanf("%d",&data);
        if(ret == 0)
            break;
        link_del(head,data); 
        display(head);
    }
    getchar();
    int old,new;
    while(1)
    {
        printf("请开始替换\n");
        ret = scanf("%d%d",&old,&new);
        if(ret == 0)
            break;
        link_replace(head,old,new); 
        display(head);
    }
    return 0;
}

综上。希望该内容能对你有帮助,感谢!

以上。仅供学习与分享交流,请勿用于商业用途!转载需提前说明。

我是一个十分热爱技术的程序员,希望这篇文章能够对您有帮助,也希望认识更多热爱程序开发的小伙伴。
感谢!

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

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

相关文章

[实用指南]如何将视频从iPhone传输到iPad

概括 将视频从 iPhone 传输到 iPad 时遇到问题&#xff1f;您可能知道一种方法&#xff0c;但不知道如何操作。此外&#xff0c;您要传输的视频越大&#xff0c;完成任务就越困难。那么如何将视频从 iPhone 传输到 iPad&#xff0c;特别是当您需要发送大视频文件时&#xff1f…

GAN对抗生成网络(一)——基本原理及数学推导

1 背景 GAN(Generative Adversarial Networks)对抗生成网络是一个很巧妙的模型&#xff0c;它可以用于文字、图像或视频的生成。 例如&#xff0c;以下就是GAN所生成的人脸图像。 2 算法思想 假如你是《古董局中局》的文物造假者&#xff08;Generator,生成器&#xff09;&a…

利用 NineData 实现 PostgreSQL 到 Kafka 的高效数据同步

记录一次 PostgreSQL 到 Kafka 的数据迁移实践。前段时间&#xff0c;NineData 的某个客户在一个项目中需要将 PostgreSQL 的数据实时同步到 Kafka。需求明确且普遍&#xff1a; PostgreSQL 中的交易数据&#xff0c;需要实时推送到 Kafka&#xff0c;供下游多个系统消费&#…

在 C# 中优化 JPEG 压缩级别和文件大小

此示例可让您检查不同 JPEG 压缩级别的图像质量。使用文件菜单的打开命令加载图像文件。然后使用“JPEG 压缩指数 (CI)”组合框选择压缩级别。程序将图像保存到具有该压缩级别的临时文件中&#xff0c;并显示生成的图像和文件大小。 该程序的关键是以下SaveJpg方法&#xff0c;…

【PCIe 总线及设备入门学习专栏 4.1 -- PCI 总线的地址空间分配】

文章目录 Overview 本文转自&#xff1a;https://blog.chinaaet.com/justlxy/p/5100053219 Overview PCI 总线具有32位数据/地址复用总线&#xff0c;所以其存储地址空间为 2324GB。也就是PCI上的所有设备共同映射到这4GB上&#xff0c;每个PCI设备占用唯一的一段PCI地址&…

Linux系统离线部署MySQL详细教程(带每步骤图文教程)

1、登录官网下载对应的安装包 MySQL :: Developer Zone 2、将压缩包上传到服务器上&#xff0c;这里直接上传到/usr/local路径上 使用sftp工具上传到/usr/local目录上 3、解压压缩包 tar -xf mysql-8.0.39-linux-glibc2.17-x86_64.tar.xz 4、将mysql-8.0.39-linux-glibc2.17…

基于python的ai五子棋游戏

游戏 运行代码后&#xff0c;点击"Enable AI Opponent"按钮&#xff0c;AI将作为白棋自动下棋。 玩家作为黑棋&#xff0c;点击棋盘落子。 AI会根据当前棋盘局势自动选择落子位置。 界面 代码 import tkinter as tk from tkinter import messageboxBOARD_SIZE 15…

STM32 软件I2C读写

单片机学习&#xff01; 目录 前言 一、软件I2C读写代码框架 二、I2C初始化 三、六个时序基本单元 3.1 引脚操作的封装和改名 3.2 起始条件执行逻辑 3.3 终止条件执行逻辑 3.4 发送一个字节 3.5 接收一个字节 3.5 发送应答&接收应答 3.5.1 发送应答 3.5.2 接…

多光谱图像的处理和分析方法有哪些?

一、预处理方法 1、辐射校正&#xff1a; 目的&#xff1a;消除或减少传感器本身、大气条件以及太阳光照等因素对多光谱图像辐射亮度值的影响&#xff0c;使得图像的辐射值能够真实反映地物的反射或发射特性。 方法&#xff1a;包括传感器校正和大气校正。传感器校正主要是根…

服务器数据恢复—离线盘数超过热备盘数导致raidz阵列崩溃的数据恢复

服务器数据恢复环境&故障&#xff1a; 一台配有32块硬盘的服务器在运行过程中突然崩溃不可用。经过初步检测&#xff0c;基本上确定服务器硬件不存在物理故障。管理员重启服务器后问题依旧。需要恢复该服务器中的数据。 服务器数据恢复环境&#xff1a; 1、将服务器中硬盘…

Jenkins管理多版本python环境

场景&#xff1a;项目有用到python3.8和3.9&#xff0c;python环境直接安装在jenkins容器内。 1、进入jenkins容器 docker exec -it jenkins /bin/bash 2、安装前置编译环境 # 提前安装&#xff0c;以便接下来的配置操作 apt-get -y install gcc automake autoconf libtool ma…

《机器学习》从入门到实战——逻辑回归

目录 一、简介 二、逻辑回归的原理 1、线性回归部分 2、逻辑函数&#xff08;Sigmoid函数&#xff09; 3、分类决策 4、转换为概率的形式使用似然函数求解 5、对数似然函数 ​编辑 6、转换为梯度下降任务 三、逻辑回归拓展知识 1、数据标准化 &#xff08;1&#xf…

开发小工具:ping地址

开发小工具&#xff1a;ping地址 import socketdef tcp_port_scan(ip,port):#创建套接字socksocket.socket(socket.AF_INET,socket.SOCK_STREAM)#设置超时sock.settimeout(0.2)try:#发请求result sock.connect_ex((ip,port))if result 0:print(f{ip}--{port}接口连接成功)res…

41.1 预聚合提速实战项目之需求分析和架构设计

本节重点介绍 : 需求分析架构设计 需求分析 使用预聚合提速查询并且降低高基数查询对后端的压力用户无需变更grafana上的查询语句&#xff0c;后端自动替换效果图 架构设计 架构图 解决方案说明 heavy_query对用户侧表现为查询速度慢在服务端会导致资源占用过多甚至打挂…

同三维T80004ES H.265高清SDI编码器

1路SDI 1路3.5音频输入,1路SDI环出 产品简介&#xff1a; 同三维T80004ES高标清SDI音视频编码器支持1路高清或1路标清SDI音视频&#xff0c;1路3.5MM独立音频接口采集功能。编码输出双码流H.265/H.264格式&#xff0c;音频 MP3/AAC格式。编码码率可调&#xff0c;画面质量可控制…

C#高级篇 反射和属性详解【代码之美系列】

&#x1f380;&#x1f380;&#x1f380;代码之美系列目录&#x1f380;&#x1f380;&#x1f380; 一、C# 命名规则规范 二、C# 代码约定规范 三、C# 参数类型约束 四、浅析 B/S 应用程序体系结构原则 五、浅析 C# Async 和 Await 六、浅析 ASP.NET Core SignalR 双工通信 …

MySQL5.7.26-Linux-安装(2024.12)

文章目录 1.下载压缩包1.访问MySQL版本归档2.找到5.7.26并下载3.百度网盘 2.Linux安装1.卸载原来的MySQL8.0.26&#xff08;如果没有则无需在意&#xff09;1.查看所有mysql的包2.批量卸载3.删除残留文件**配置文件**&#xff08;默认路径&#xff09;&#xff1a; 4.**验证卸载…

数据分析思维(六):分析方法——相关分析方法

数据分析并非只是简单的数据分析工具三板斧——Excel、SQL、Python&#xff0c;更重要的是数据分析思维。没有数据分析思维和业务知识&#xff0c;就算拿到一堆数据&#xff0c;也不知道如何下手。 推荐书本《数据分析思维——分析方法和业务知识》&#xff0c;本文内容就是提取…

前 5 名 IPhone 解锁工具/软件

设备已禁用并且您无法访问它&#xff1f;如果您无法通过密码解锁&#xff0c;尝试 iPhone 解锁软件可能是最好的解决方案。 虽然市场上有很多免费或付费的 iPhone 解锁工具&#xff0c;但您可能不知道它们之间的区别以及如何选择最适合您的工具。 本文将介绍 5 款iPhone 解锁…

【LeetCode Hot100 回溯】组合、排列、子集、分割、N皇后、单词搜索、括号生成、电话号码的字母组合

回溯 组合问题组合总和全排列子集分割回文串N皇后电话号码的字母组合单词搜索括号生成 组合问题 给定两个整数 n 和 k&#xff0c;返回 1 … n 中所有可能的 k 个数的组合。 示例: 输入: n 4, k 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 树形结构&#xff1…