链表各种操作

来源:互联网 发布:数据库编程软件有哪些 编辑:程序博客网 时间:2024/05/01 20:15

最简单的操作无非是以下几点:create  search  insert  delete

1.创建一个列表并且遍历它,打印出各节点的值

复制代码
struct node{    int data;    node* next;};node* init_node(int value){    node* head = new node();    head->data = value;    head->next = NULL;    return head;}void create_list(node* head,int value){    node* current_node = head;    node* new_node = init_node(value);    while(current_node->next!=NULL)    {        current_node = current_node->next;    }    current_node->next = new_node;}node* init_list(int* arr,int len){    node* head = init_node(0);    for(int i=0;i<len;i++)    {        create_list(head,arr[i]);    }    return head;}void list_traversal(node* head){    node* current_node = head;    while(current_node->next!=NULL)    {        current_node = current_node->next;        cout<<current_node->data<<" ";    }}
复制代码

关于初始化过程,不要放在main函数中进行。

2.查找一个结点

复制代码
void search_node(node* head,int value){    node* current_node = head;    while(current_node->data!=value && current_node->next!=NULL){        current_node = current_node->next;    }    if(current_node->data==value){        cout<<endl<<"search node data is "<<current_node->data<<endl;    }else{        cout<<endl<<value<<" is not in the list"<<endl;    }}
复制代码

3.插入结点

当插入一个结点时,可以选择插入到当前结点的前面或者后面。

复制代码
void insert_node_beforeVar(node* head,int var,int value){    node* current_node = head;    node* pre_node;    node* new_node = init_node(value);    while(current_node->data!=var && current_node->next!=NULL){        pre_node = current_node;        current_node = current_node->next;    }    if(current_node->data==var){        pre_node->next = new_node;        new_node->next = current_node;    }}void insert_node_behindVar(node* head,int var,int value){    node* current_node = head;    node* new_node = init_node(value);    while(current_node->data!=var && current_node->next!=NULL){        current_node = current_node->next;    }    if(current_node->data==var){        node* next_node = current_node->next;        current_node->next = new_node;        new_node->next = next_node;    }}
复制代码

4.删除一个结点

复制代码
void delete_node(node* head,int value){    node* current_node = head;    node* pre_node;     while(current_node->data!=value && current_node->next!=NULL){        pre_node = current_node;        current_node = current_node->next;    }    if(current_node->data==value){        node* next_node = current_node->next; //删除结点导致链表断开,因此要保存下一个结点        pre_node->next = next_node;        delete(current_node);    }}
复制代码

下面总结下关于链表的各种算法,都是在平时做题时遇到的,参考了网上很多DS的杰作,在此感谢各位DS。

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

 换个思路,柳暗花明。


0 0
原创粉丝点击