C++关于链表基础知识

单链表

// 结点的定义

template <class T>

struct Node

{

T data ;

Node <T> *next; //指向下一个node 的类型与本node相同

}

// 最后一个node指针指向Null

生成结点: Node <T> * p = new Node < T>;

为结点赋值: p-> data = 'a';

p- > next = (其他结点的地址); p -> next = NULL ; (尾结点)

delete p; //释放结点

// 单链表的类实现

template <class T>

class LinkList

{

public : //此处是函数定义

LinkList();

ListList(T a[ ] , int n);

int Lenght();

T Get(int); //俺下标查找

int Loate(T); //按值定位

void insert(T,int); //插入

T delete (int) ; //删除

~LinkList();

private:

Node <T> * first;

}

构造函数的实现一个空链表:

cpp 复制代码
template <class T>
LinkList <T> :: LinkList()
{
    first = new Node <T>;    
    first->next = NULL;           
}

头插法

初始化头节点(头节点指的是first,首结点代表链表第一个结点)

cpp 复制代码
template <class T>
LinkList <T> :: LinkList (T a[ ] ,int n)
{
    //初始化头节点
    first = new Node<T>;
    first ->next =NULL;
    for (int i =0 ; i<n ;i++)
    {
        //生成新的结点
        Node <T> *s = new Node <T>;
        s ->data =a [i]; 
        
        //链接再头节点和首结点之间
        s->next = first ->next;
        first ->next = s ;
    }
}

核心代码:

s->next = first ->next;

first ->next = s ;

因为对于first ->next的初始化指向是NULL;此处赋值给s->next则表示了s->next指向了NULL

然后将地址s赋值给了first ->next(头节点的地址域)

尾插法

cpp 复制代码
template <class T>
LinkList <T> :: LinkList (T a[ ] ,int n)
{
    //初始化头节点
    Node<T> *first = new Node<T>;
    Node<T> *r = first;    //尾结点首先指向first
    for (int i =0 ; i<n ;i++)
    {
        //生成新的结点
        Node <T> *s = new Node <T>;
        s ->data =a [i];
        s ->next =NULL;   //由于是尾插,直接指向NULL 
        
        //链接在尾结点后面:
        r->next = s;
        //尾结点后移,由于此时的新的结点s是尾结点
        r= s;
    }
    //这里也可以加一句r->next = NULL;
}

析构函数:

cpp 复制代码
template <class T>
LinkList <T> :: ~linkList
{
    while (first != NULL)   //或者直接while(first)
    {
        Node <T> *p = first;
        first = first->next;
        delete p; 
    }
}

按照值查找:

查找值为x的结点的位置

顺序查找:

cpp 复制代码
template <class T>
int LinkList <T> :: locate (T x)  // T类型的x
{
    Node<T> *p = first->next ;//首结点后的第一个结点
    int j=1;
    while(p)   //p不为空一直循环,p为NULL的时候即循环到结尾

    {
        if(p->data ==x ) return j;
        p = p->next ;
        j++;
    }
    return 0;
}

按照值查找返回结点 ,非模板类实现:

cpp 复制代码
Node * LinkList:: local (int val )
{
    Node* p = first;
    while (p )
    {
        if (p ->data == val)
        {
            return p;
        }
        p = p -> next;
    }
    return NULL;
}

按下标查找:

cpp 复制代码
template <class T>
T LinkList <T> :: Get (int i){
//初始化
    Node<T> * p = first ->next ;
    int j =1;
    while(j<i && p!=NULL)  
    {
        p= p->next
        j++;
    }
    if(p==NULL || i<1) throw"位置非法";
    return p->data ;
}

随意位置插入:

思路:

新建结点 Node<T> *s = new Node <T>; s->data = x;

插入 : s ->next = p->next ; (p->next 的值是ai的地址,将其赋值给新节点地址域)

p->next =s ;

cpp 复制代码
//将值为x的结点插入到位置为i的结点,那么首先要找到i-1 号结点
template <class T>
void LinkList <T> :: Insert (T x , int i)
{
    Node <T> * p = first ; 
    int j = 0 ;
    while (p && j<i-1)
    {
        p = p->next ; 
        j++;
    }
    if (p == NULL) throw "插入位置非法"
    else {
    // 新建结点 
       Node<T> *s = new Node <T>;
       s->data = x;      
    //插入
       s ->next = p->next ; 
       p->next =s ;
    }
}

在指定结点后插入:

cpp 复制代码
//在指定的p结点之后插入
template <class T>
void LinkList <T> :: insertAfter (Node<T> * p , T x )
{
    Node <T> *s =  new Node <T>;
    s -> data =x ;
    s->next = p->next; 
    p- next = s ;
}

//在指定的p结点之前插入
//需要先找到p结点的前面一个结点q ,循环来找
template <class T>
void ListList <T> ::insertBefore (Node<T>  * p ,T x )
{
    Node <T> *q = first ; //从头节点开始找
    while (q ->next != p ){   // 找到q结点的条件
       q =q->next ; 
    }
    insertAfter (q,x);  //在q后插入,等于在p前插入
}
//上述方法时间复杂度是O(n)
//下列实现时间复杂度是O(1)
template <class T>
void LinkList <T> :: insertBefore (Node <T> * p ,T x)
{
    Node <T> *s = new Node<T>;
    * s = *p ;
    p ->next = s;    //将原来的p结点当成新插入的结点,把新的结点变为p结点放在后面
    p ->data = x;    // 等同于前插
}

结点删除:

首先需要把ai代表的结点摘除,使用s 表示:

s = p->next ;

p->next = s -> next ;

// 或者这里直接 : p ->next = p.next.next; 后续就不需要删除新建结点s了

然后删除结点:

delete s ;

cpp 复制代码
template <class T>
T LinkList <T> ::  detele (int i)
{
    Node <T> * p = first ;
    int j = 0;
    while(p  && j <i )
    {
        p = p->next ;
        j++ ;
    }
    if (p->next ==NULL)
        throw "删除位置非法"
    else {
        s = p->next ;   //让中间结点(待删除)赋值给s
        p->next = s ->next ;  // 然后第三结点赋值给第一个结点的地址域
        delete s; //删除中间结点
    }
}
// 上述操作时间复杂度O(n)
//改成O(1):
template <class T>  //返回T因为一般删除都会返回结点的x值
T LinkList <T> ::delete (Node <T> *p )
{
    if(p->next){// p非最后一个结点 ,使用删除后面的一个结点代替删除自己
        T x =p ->data ;
        p -> data = p ->next ->data ;
        Node <T>  *q = p ->next ;  //表示出来后一个结点
        q ->next = p ->next  ; //准备将q出链
        delete q;
        return x ;//一般删除结点都返回其值
    else{...正常循环}
}
}

或者非模板的普通函数实现://输入是指针

cpp 复制代码
void delNode (ListNode *prev)
{
    ListNode * curr = prev -> next;
    prev->next = curr -> next ;
    delete curr;
}

求取链表长度:

cpp 复制代码
template <class T>
int LinkList <T> :: Length(){
    Node <T> * p = first ;
    int i =0;
    while ( p ){
        p = p ->next;
        i++;
    }
    return i;
}
//上述时间复杂度 O(N)

反转链表(力扣206题):

在原链表直接反转:

cpp 复制代码
template <class T>

LineList<T>* LinkList<T> :: reverse(LineList * head){

    //当结点只有一个或者时一个空节点的时候不需要反转直接返回
    if(head == NULL || head->next == NULL ) return head;
    
    Node<T> *a = head , *b = head->next ;

    while(b) {
        Node<T> * c= b->next ;   //首先需要c来维持原链表的数据获取 
        //开始交换
        b->next = a;
        a =b ;  //把b指针赋值给a指针, 此时a指向第二个元素
        b =c ;  //此时b指向第三个元素
    }
    head -> next = NULL;

    return a;  //最后返回新的头节点
} 

力扣206原题:

cpp 复制代码
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head ==nullptr || head->next ==nullptr) return head;
        auto *a = head;
        auto *b =head->next;
        while (b){
            auto *c = b->next;
            b -> next= a;
            a = b ;
            b =c ;
        }
        head->next =nullptr;
        return a;
    }
};

合并链表(力扣21题):

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* list3 = new ListNode;
        //获取头结点
        auto *head1 = list1;  
        auto * head2 = list2;
        auto * head3 = list3;
        while(head1 != nullptr && head2 !=nullptr)
        {
            if(head1->val <= head2->val)
            {
                head3->next = head1;
                head1 = head1->next;
            }
            else   {
                head3 ->next = head2;
                head2 =head2 ->next;
            }
            head3 = head3->next;
        }
        //若有l1和l2任一链表循环到头,另一直接全部剩余插入l3
        head3->next  = head1==nullptr? head2:head1;
        return list3->next;        
    }
};

循环链表:

尾指针地址域指向头节点,上述代码循环条件改为p!= r

双链表:

每个结点有两个指针域,指向前驱和后继

cpp 复制代码
template <class T> 
struct DulNode 
{
    T data;
    DulNode <T> * prior, *next ;
}

插入操作:

首先更改新结点的指针域

然后动链表

删除操作:

顺序表和链表的对比:

相关推荐
猪蹄手13 分钟前
C/C++复习(二)
开发语言·c++
蓝天扶光43 分钟前
c到c++衔接速成
c语言·开发语言·c++
it资源网1 小时前
Go-Micro客户端请求报500错误的解决方法
开发语言·后端·golang
拒绝头秃从我做起1 小时前
输出平方矩阵
c语言·开发语言
好青崧1 小时前
JavaScript 中的变量作用域
java·开发语言·javascript
通信仿真实验室1 小时前
(19)MATLAB使用Nakagami 分布对象生成Nakagami-m分布
开发语言·算法·matlab
霍金的微笑1 小时前
牛客练习题
java·开发语言
寂柒1 小时前
C++——模拟实现list
c++·windows·list
wellshake1 小时前
Pyppeteer:如何在 Python 中使用 Puppeteer 和 Browserless?
开发语言·python·node.js