C++ 链表总结

一、单链表问题

定义ListNode节点结构体

struct ListNode {
     int val;
     ListNode *next;		//定义节点的后继
     ListNode(int x) : val(x),next(NULL)//声明一个节点
}

1、链表反转

 1->2->3->4->5->null
 ^
 root

  逆序的思路:首先声明一个空指针node指向一个空节点,然后将原链表中的节点1指向node,接着声明一个临时根节点root用来保存链表的表头,循环操作,直到root为空指针
示例程序如下:

ListNode *reverse(ListNode *root)
{
    ListNode *node = nullptr;
    while(root){
        ListNode *next = root->next;	//保存表头的位置
        //由于逆序,因此此时原表头的下一个节点为空节点 ,下一个node为原表头
        root->next = node;				
        node = root;					//保存此时的根节点,准备下一次连接
        root = next;					//重置新的表头
    }
    return node; 
}

程序执行流程如下:

//step1: 保存表头的位置
ListNode *next = root->next;
2->3->4->5->null
^
next
//step2:连接新的链表
root->next = node;
1->null
^
root 
//step3:更新待连接的节点位置
node = root;
1->null
^
node 
//step4:更新表头位置
root = next;
2->3->4->5->null
^
root
//循环操作,直到root为null

2、链表去重

  去除链表中重复的数字,分两种情况:1,链表是有序链表;2,链表是无序链表

2.1有序链表去重

  有序链表:即链表中的元素按照一定顺序排列,链表中的相同元素是相邻的,比如升序,降序。
eg:

 1->2->2->3->4->5->null   | 1->2->2->2->3->4->null
 ^                        | ^
 root                     | root

  算法思路:首先明确判断的条件:相同的元素是相邻的,即cur->val == cur->next->val,找到这个元素后,先将该元素(cur->next)的后继节点连接到一个临时节点保存起来,然后删除cur->next,此时cur->next为空,最后将临时节点连接到当前cur的后继。此处不能直接将cur->next的后继节点赋值给cur->next,也不能首先删除cur->next,一定要缓存cur->next

//code:
void removeDuplicates(ListNode *head) {
    if (head == nullptr) return;		//若是空指针,则直接跳出
    for (ListNode *cur=head; cur->next; )    //拷贝head到cur,判断cur->next是否为空,为空就跳出循环;
        if (cur->val == cur->next->val) {    //比较条件
            ListNode *next = cur->next->next;    //临时缓存cur->next的后继节点
            delete  cur->next;        //删除重复的节点,释放内存
            cur->next = next;        //将cur->next的后继节点连接到cur的后继
        } else 
        { 
            cur = cur->next;    //比较下一个节点
        }
}

2.2无序链表去重

  无序链表:即链表中的元素顺序是无规则的,相同元素是不相邻的,此时删除的是后面重复的元素。
eg:

 1->2->3->2->4->5->null   | 1->2->3->2->1->3->null
 ^                        | ^
 root                     | root

  算法思路:首先明确判断的条件:寻找后续的节点中的元素与当前节点是否相同,寻找到相同的元素后删除节点,继续寻找剩下的元素中是否有相同的元素。

//code1:时间复杂度O(N^2),空间复杂度O(1)
void removeRep1(ListNode* head){
        if(head == nullptr){
            return;
        }
        ListNode* cur = head;
        ListNode* pre = nullptr;
        ListNode* next = nullptr;
        //选中一个节点,逐个比较后面是否有重复的节点,有则删除
        while(cur != nullptr){
            pre=cur;
            next=cur->next;
            while(next != nullptr){
                if(cur->val==next->val){    //后面next的节点等于当前cur节点,删除next节点
                    pre->next = next->next;
                    delete next;    //删除重复的节点
                    next = pre;     //将链表重新连接起来
                }
                else
                {
                    //不重复
                    pre = next;
                }
                next = next->next;
            }
            cur = cur->next;//再以下一个节点作为参考点
        }
    }

  另外一种思路,利用容器的find功能进行遍历

//code2:时间复杂度O(N),空间复杂度O(N)
void removeRep1(ListNode *head){
        if(head==nullptr){
            return;
        }
        set<int> val_set;    //利用容器find的功能
        ListNode *pre = head;    //声明前一个节点
        ListNode *cur = head->next;    //声明当前比较的节点
        val_set.insert(pre->val);    //头结点加到容器列表中
        while(cur != nullptr)
        {
            //判断容器中是否已存在cur->val,不存在,就将该元素添加到容器中
            if(val_set.find(cur->val) == val_set.end())
            {
                val_set.insert(cur->val);
                pre = cur;     //更新上一个节点
            }
            else
            {
                pre->next = cur->next;    //删除重复节点
                delete cur;               //删除重复的节点的内存
                cur = pre;                //将链表重新连接起来
            }
            cur = cur->next;              //再以下一个节点作为参考点
        }           
    }

3、链表合并

  链表合并,即将两个链表合并为一个链表,不是简单的首尾合并,示例如下:

1->2->3
^
a_root
            ==>    1->4->2->5->3->6
4->5->6            ^
^                  new_list
b_root

  算法思路:在程序中使用dummy节点来缓存节点,其作用是保存新的链表,头节点的值可任意设置,最后返回dummy.next即可。

//code:
ListNode *shuffleMerge(ListNode *a, ListNode *b) {
    ListNode dummy(0), *tail = &dummy;
    while (a && b) {
        tail->next = a;        //初始时tail->next为空,以后的循环中令上一轮中的节点b指向下一个a
        tail = a;              //更新tail的地址,以便下一次的拼接
        a = a->next;           //处理a的下一个节点
        tail->next = b;        //上一轮中的节点a指向下一个b
        tail = b;              //更新tail的地址,以便下一次的拼接
        b = b->next;           //处理b的下一个节点
    }
    tail->next = (a ? a : b);    //若a和b不是等长的,将剩余部分拼接到tail的末尾
    return dummy.next;           //返回拼接好的节点的头结点
}

4、链表顺序插入

  给一个有序链表root,先有一个新节点需要插入该列表中。

4
^
newNode
               ==>  1->3->4->5->7->8
1->3->5->7->8
^
root

  算法思路1:直接法,遍历整个链表,找到newNode合适的位置插入

//code:
void sortinsert(ListNode **root, ListNode *newNode) {
     if (*root ==  nullptr || (*root)->val >= newNode->val) {
         newNode->next = *root;
         *root = newNode;       //更新链表root   
     }
     else
     {
         ListNode *cur = *root;
         //如果节点cur不为空,而且下一个节点的值小于newNode的值,则移动cur的位置
         while(cur->next != nullptr && cur->next->val < newNode->val)
         {
            cur = cur->next;
         }
         //否则,将新节点插入当前位置
         newNode->next = cur->next;
         cur->next = newNode;       
     }
     
 }

  算法思路2:和引用类似的思路,不过不再是cur的移动,而是指针的移动

//code:
 void sortinsert(ListNode **root, ListNode *newNode) {
     ListNode **cur = root;//拷贝更节点
     //如果节点*cur不为空,而且下一个节点的值小于newNode的值,则移动*cur的位置
     while(*cur != nullptr && (*cur)->val < newNode->val){
        cur = &((*cur)->next);       
     }
    //否则,将新节点插入当前位置
    newNode->next = *cur;
    *cur = newNode;     
 }

5、链表排序

  根据上述的顺序插入,链表的排序问题关键在于如何插入元素
  算法思路:声明一个空链表ListNode *newroot = nullptr,然后遍历整个链表,再用上一节提到的顺序插入函数,将每一个节点用sortinsertnewroot

void insertSort(ListNode **head) {
    ListNode *newHead = nullptr;        //声明空节点
    ListNode *curr = *head, *next;      //声明一个cur和一个临时节点
    //若当前节点不为空
    while (curr) {      
        next = curr->next;              //缓存后续的节点    
        sortinsert2(&newHead, curr);    //处理当前的节点
        curr = next;                    //更新当前节点,准备处理下一个节点
    }
    *head = newHead;                    //更新链表
}

猜你喜欢

转载自blog.csdn.net/qq_18150255/article/details/88636821
今日推荐