算法第三天|链表

本文介绍了链表的基本概念,包括单链表、双链表和循环链表的特点,以及如何在C和Python中实现链表节点和移除元素的操作。还涵盖了设计链表的方法,如虚拟头节点和链表的反转算法。
摘要由CSDN通过智能技术生成

前言

打卡第三天,加油!


链表知识

【链表 linked list】是一种线性数据结构,其中的每个元素都是一个节点对象,链表是一种通过指针串联在一起的,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。链表的入口节点称为链表的头结点也就是head。

各个节点通过“引用”相连接。引用记录了下一个节点的内存地址,通过它可以从当前节点访问到下一个节点。链表的设计使得各个节点可以分散存储在内存各处,它们的内存地址无须连续。

单链表

双链表

单链表中的指针域只能指向节点的下一个节点。

双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。

双链表 既可以向前查询也可以向后查询。

循环链表

循环链表,顾名思义,就是链表首尾相连。循环链表可以用来解决约瑟夫环问题。

代码实现

链表结点的定义

C

//结构体定义
typedef struct ListNode{
    int element;
    struct ListNode *next;}ListNode;

Python

class ListNode:
    def __init__(self,int val):
        self.val = val
        self.next = None

移除链表元素

203. 移除链表元素icon-default.png?t=N7T8https://leetcode.cn/problems/remove-linked-list-elements/

删除值与target相等的结点有两种情况

1,不设置新的头结点,直接使用原来的链表来进行移除。

删除结点的时候需要找到被删除结点的上一个结点,但是头结点没有,所以需要为删除头结点单独写一段代码

当头结点可以保留的时候,可以开始比较头结点后的结点,因为返回链表需要返回头结点,所以在遍历链表的时候不能通过头结点来遍历,需要再新建一个指针来进行链表的遍历和结点的删除。

2,创建一个新的头结点        

当创建一个新结点后,该结点指向头结点,这样可以使链表的所有结点删除操作变的相同

但是结点的遍历任然需要新设置一个遍历指针来实现,理由同上,头指针需要被用来返回链表时候使用

C语言        【原链表版】

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode *temp;      
    while(head&&head->val==val){
        temp = head;
        head = head->next;
        free(temp);
    }
    struct ListNode *cur = head;
    while(cur&&(temp=cur->next)){
        if(temp->val == val){
            cur ->next = cur->next->next;
            free(temp);
        }
        else{
            cur= cur->next;
        }
    }
    return head;
}

C语言        【设置头指针版】

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val) {
    typedef struct ListNode ListNode;
    ListNode *newNode;
    newNode = (ListNode *)malloc(sizeof(ListNode));
    newNode->next = head;
    ListNode *cur = newNode;


    while(cur->next != NULL){
        if(cur->next->val == val){
            ListNode *temp;
            temp = cur->next;
            cur->next = cur->next->next;
            free(temp);
        }
        else{
            cur = cur -> next;
        }
    }
    head = newNode->next;
    free(newNode);
    return head;
}

设计链表

707. 设计链表icon-default.png?t=N7T8https://leetcode.cn/problems/design-linked-list/




typedef struct {
    int val;
    struct MyLinkedList* next;
}MyLinkedList;

 //这个题必须用虚拟头指针,参数都是一级指针,头节点确定后没法改指向了!!!
MyLinkedList* myLinkedListCreate() {
    MyLinkedList *head = (MyLinkedList*)malloc(sizeof(MyLinkedList));
    head ->next = NULL;
    return head;
}

int myLinkedListGet(MyLinkedList* obj, int index) {
    MyLinkedList* cur = obj->next;
    for(int i=0;cur!=NULL;i++){
        if(i == index){
            return cur->val;
        }
        else{
            cur = cur->next;
        }
    }
    return -1;
}

void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
    MyLinkedList *new = (MyLinkedList*)malloc(sizeof(MyLinkedList));
    new->val = val;
    new->next = obj -> next;
    obj->next = new;
}

//单链表实现尾插法必须先新建一个指针,遍历到链表尾部,再进行插入
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
    MyLinkedList *cur = obj;        //注意下指针为什么是从虚拟头指针开始
    while(cur->next != NULL){
        cur = cur ->next;
    }
    MyLinkedList *new = (MyLinkedList*)malloc(sizeof(MyLinkedList));
    new->val = val;
    cur->next = new;
    new->next = NULL;
    
}

void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
    if(index ==0){
        myLinkedListAddAtHead(obj,val);
        return;
    }
    MyLinkedList *new = (MyLinkedList*)malloc(sizeof(MyLinkedList));
    new->val = val;
    MyLinkedList *cur = obj->next;
    for(int i= 1; cur!=NULL;i++){
        if(i == index ){
            new->val = val;
            new->next = cur->next;
            cur->next = new;
        }
        else{
            cur = cur->next;
        }
    }
}

void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
    if(index == 0){
        MyLinkedList *cur0 = obj-> next;
        if(cur0 != NULL){
            obj->next = cur0 ->next;
            free(cur0);
        }
    }

    MyLinkedList *cur = obj-> next;
    for(int i=1;cur!=NULL&& cur->next != NULL;i++){
        if(i == index){
            MyLinkedList*dnode = cur->next;
            if(dnode != NULL){
                cur ->next = dnode->next;
                free(dnode);
            }
            return;
        }
        else{
            cur = cur->next;
        }
    }

}

void myLinkedListFree(MyLinkedList* obj) {
    while(obj != NULL){
        MyLinkedList *tmp = obj;
        obj = obj->next;
        free(tmp);
    }
}

/**
 * Your MyLinkedList struct will be instantiated and called as such:
 * MyLinkedList* obj = myLinkedListCreate();
 * int param_1 = myLinkedListGet(obj, index);
 
 * myLinkedListAddAtHead(obj, val);
 
 * myLinkedListAddAtTail(obj, val);
 
 * myLinkedListAddAtIndex(obj, index, val);
 
 * myLinkedListDeleteAtIndex(obj, index);
 
 * myLinkedListFree(obj);
*/

反转链表

206. 反转链表icon-default.png?t=N7T8https://leetcode.cn/problems/reverse-linked-list/

首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。

然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。

为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。

接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。

最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。

#双指针法

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {

    struct ListNode *pre =NULL;
    struct ListNode *cur;
    while(head){
        cur = head->next;
        head->next = pre;
        pre = head;
        head = cur;
    }
    return pre;


}

这里在定义cur指针不能直接赋值是因为cur的目的是保存下一个待翻转的结点的地址,该地址在循环中是不断变化的,即使是赋值也会被覆盖

  • 30
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值