C++链表

本文总结了C++中链表的各种操作,包括创建、删除、插入、输出、排序、反序、遍历、链表逆置、判断链表是否相交、检查环路、寻找倒数第k个节点、链表排序以及合并有序链表等。通过递归和迭代等方式,详细阐述了这些操作的实现原理和技巧。

链表操作总结:

               链表是一种重要的数据结构。它是动态分配存储的一种结构。它可以根据需要开辟存储单元。

链表有一个“头指针”变量,以head表示,它存放一个地址。该地址指向一个元素,链表中的每一个元素

称为“结点”,每个结点包含两部分:1.实际数据data   2.下一个结点的地址next。表尾的地址部分存放“NULL”.

表示链表到此结束。

     链表的各类操作包括:学习单向链表的创建、删除、  插入(无序、有序)、输出、  排序(选择、插入、冒泡)、反序等等。

      那么链表该如何遍历呢,遍历链表需要从头到尾,访问每一个元素,直到链表尾。也就是说不断地访问当前节点的next,直到NULL。下面是链表的遍历输出:

 

#include <iostream>
using namespace std;
class node
{
public:
	int value;
	node *next;
	node()
	{
		value = 0;
		next = NULL;
	}
};
int main()
{	
	node *head,*curr;
	head = new node();
	head->next = NULL;
	head->value = 15;
	for (size_t i = 0; i < 10; i++)
	{
		curr = new node();
		curr->value = i;
		curr->next = head;
		head = curr;
	}
	while (head!=NULL)
	{
		cout << head->value << endl;
		head = head->next;
	}
}

链表相对于数组有个非常明显的优点就是能以时间复杂度o(1)完成一个节点的插入或者删除操作。

1.递归实现单链表逆置

tips:

(1)逆置不是指将原有链表逆序打印,逆置破坏了原有链表的结构;

(2)既然需要用递归实现,那首先要明白递归的本质。

  我在接触递归时,常常纠结与递归的实现过程,根本没有理解递归的本质,我现在觉得,递归只需要关注以下几点:

  • 算法是否可以用递归实现。感觉递归类似于循环,条件允许范围内一直做某事;
  • 递归时,函数做了哪些操作
  • 递归的终止条件
node* list_reverse(node* head)
{
    if(head==NULL || head->next==NULL)
    return head;
  //每次函数执行时带来的next_node都依次入栈
  //最后栈顶保存的是原链表的最后一个结点
    node* next_node = list_reverse(head->next);
    head->next->next = head;
    head->next = NULL;
    return next_node;
}

2.判断两个链表是否相交

思路:如果两个链表相交了,那么交点肯定在最后一个结点,因此问题转化为求链表最后一个结点。

各自求显得麻烦,可以先判断链表的长度,len1 和 len2,长的链表先走 | len1 - len2 |步,然后开始同时走。

int length_list(node* head)
{
    int count=0;
    node* current_node = head;
    if(current_node==NULL)
        return 0;
    else if(current_node->next==NULL)
        return 1;
    else{
        while(current_node->next!=NULL){
            count++;
            current_node = current_node->next;
        }
    }
    return count;
}

void intersection_twoLists(node* head1,node* head2)
{
    if(head1==NULL || head2==NULL)
    return;
    else{
        int length1 = length_list(head1);
        int length2 = length_list(head2);
        int step;
        if(length1 >= length2){
            step = length1 - length2;
            for(int i=1;i<=step;i++){
                head1 = head1->next;
            }
        }else{
            step = length2 - length1;
            for(int i=1;i<=step;i++){
                head2 = head2->next;
            }
        }
        while(head1->next!=NULL && head2->next!=NULL){
            head1 = head1->next;
            head2 = head2->next;
        }
        if(head1->data == head2->data){
            cout<<"intersection";
        }else{
            cout<<"not intersection";
        }
    }
}

3.判断链表是否有环

(1)最简单的一种情况就是链表最后一个结点指向了head,如果最后一个结点指向了null,则没有环路,否则会一直绕圈圈。

void loop_list_VersionFirst(node* head)
{
    node* current_node = head;
    while(current_node->next!=NULL){
        current_node = current_node->next;
    }
    if(current_node->next==head){
        cout<<"has loop"<<endl;
    }else{
        cout<<"no loop"<<endl;
    }
}

(2)链表并非从一开始就进入环路,而是从中间某一结点开始

  • 想法1:最笨的方法就是将current_node与之前的结点比较,如果发现曾经出现过,那么就存在环路。但这种方法需要将之前遍历过的结点全部保存下来,然后依次与当前结点进行比较,很麻烦。
  • 想法2:p1->next , p2->next->next

假设链表有n个结点,未进入环路的结点有k个,则存在于环路中的结点为(n-k)个

现假设p1 p2都已经进入了环路,那么在t时刻,

p1->(v*t+1)%(n-k) 

p2->(2*v*t+1)%(n-k)

如果存在环路,则 (v*t+1)%(n-k) = (2*v*t+1)%(n-k) 在t 时刻,p1和p2相遇

bool hasLoop_list(node* head)
{
    bool loop = false;
    node* p1 = head;
    node* p2 = head;
    if(p1==NULL || p1->next==NULL || p1->next->next==NULL)
        return false;
    else{
        while(p2->next!=NULL){
            p1 = p1->next;
            p2 = p2->next->next;
        }
        if(p1 == p2)
            loop = true;
    }
    return loop;
}

4.求链表的倒数第k个结点

这道题目可以转化为求正数第(n-k)个结点

int node_countFromEnd(node* head,int k)
{
    int step = length_list(head)-k;
    //cout<<length_list(head);
    //cout<<"step is "<<step;
    node* current_node_k = head;
    for(int i=0;i<step;i++){
        current_node_k = current_node_k->next;
    }
    return current_node_k->data;
}


5.单链表排序

如果直接对链表的元素进行排序,那就需要交换指针,这样会很麻烦,其实是我不擅于操作指针罢了,那么对于一个菜鸟来说,最方便的就是将链表中的元素放到数组中,然后再对数组中元素进行快排。但这样需要另外开辟空间。

首先附上快排的代码:

int middle_PathQucik(int* arr,int start_index,int end_index)
{
    int i = start_index-1;
    int flag = arr[end_index];
    for(int j=start_index;j<end_index;j++){
        if(arr[j]<flag){
            i+=1;
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    int temp = arr[i+1];
    arr[i+1] = arr[end_index];
    arr[end_index] = temp;
    return i+1;
}
void quick_sort(int* arr,int start_index,int end_index)
{
    if(start_index>=end_index)
    return;
    int middle = middle_PathQucik(arr,start_index,end_index);
    quick_sort(arr,start_index,middle-1);
    quick_sort(arr,middle+1,end_index);
}

我们仅需要做的就是将链表中元素放到数组中,然后数组再形成新的有序链表。

void list_sort(node* head)
{
    node* current_node = head;
    int length_arr = length_list(head);
    int arr[length_arr];
    for(int i=0;i<length_arr;i++){
        current_node = current_node->next;
        arr[i] = current_node->data;
    }
    quick_sort(arr,0,length_arr-1);
    node* new_head = init_list(arr,length_arr);
    list_traversal(new_head);
}

 6.合并两个有序链表

这道题目就相当于把一个结点插入有序链表,时间复杂度为O(length1)+O(length2)

把一个结点插入有序链表的代码如下:

void elementInsertOrderList(node* head,int value)
{
    node* current_node = head;
    node* pre_node;
    node* insert_node = init_node(value);
    while(current_node->next!=NULL){
        pre_node = current_node;
        current_node = current_node->next;
    }
    if(current_node->data>=value){
        pre_node->next = insert_node;
        insert_node->next = current_node;
    }else{
        current_node->next=insert_node;
    }
    list_traversal(head);
    cout<<endl;
}

有了上述代码,合并两个有序链表就变得非常简单了。

void mergeTwoOrderLists(node* head1,node* head2)
{
    node* current_node = head2;
    int length2 = length_list(head2);
    for(int i=0;i<length2;i++){
        current_node = current_node->next;
        elementInsertOrderList(head1,current_node->data);
    }
}


 

 6.删除当前current_node

与正常删除链表结点不同,这道题未提供头指针,所以比较有意思。

void delete_currentnode(node* current_node){
    node* next_node = current_node->next;
    current_node->data = next_node->data;
    current_node->next = next_node->next;
    delete(next_node);
}


 

由于只知道current_node,它之前的结点无法得知,所以无法用pre_node->next = current_node->next;

但是可以用current_node->next = current_node->next->next;delete current_node->next

 

 



 






 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值