单链表(算法面试题2)---单链表进阶2 一题多解,逐步优化

简介: 单链表(算法面试题2)---单链表进阶2 一题多解,逐步优化

往期链表文章:(如果想更多的了解单链表,笔者建议可以简略的了解往前的文章)

单链表(面试算法题1)---学习链表的关键在于code

单链表(面试算法题2)---单链表进阶1之快慢指针

创建链表、打印链表、释放内存的基础操作这里就不code了,想了解的朋友可以浏览

单链表(面试算法题1)---学习链表的关键在于code

今日份试题1:

题目描述:给定一个单链表的头节点head,请判断该链表是否为回文结构。

实现思路1:栈方法(额外空间复杂度O(N))

我们先聊下栈的特点:先进先出

abcd依次入栈,得到的出栈序列:dcba   (逆序)

对于判断回文,我们也可以先将其压入栈,然后逐个弹出与原来的序列进行比较。

bool isPalidrome1(Node head){
    if(head==NULL){
        return true;   //空串我们这里算回文串
    }
    stack<node> sta;
    Node cur = head;
    //依次压入栈
    while(cur!=NULL){
        sta.push(*cur);
        cur = cur->next;
    }
    //比较
    while(head != NULL){
        if(head->value != sta.top().value){
                return false;
        }
        sta.pop();
        head = head->next;
    }
    return true;
}

做个简单优化:其实只需要将一半的序列压入栈中就可以了,节省空间的开销。

先通过快慢指针左预处理找到中点位置,奇数长度返回中点,偶数长度返回下中点。

bool isPalidrom2(Node head){
    //如果为空
    if(head == NULL){
        return true;
    }
    //如果只有一个结点
    if(head->next == NULL){
        return true;
    }
    stack<node> sta;
    Node slow = head->next;
    Node fast = head->next;
    while(fast != NULL && fast->next != NULL){
        fast = fast->next->next;
        slow = slow->next;
    }//slow指向的就是中点
    while(slow != NULL){
        sta.push(*slow);
        slow=slow->next;
    }
    while(!sta.empty()){
        if(head->value != sta.top().value){
            return false;
        }
        head = head->next;
        sta.pop();
    }
    return true;
}

实现思路2:改变链表法

先通过快慢指针找到中点,然后改变中点以后的结点的next指向,使其指向反方向。然后一个从头部一个从尾部开始,比较是否相等,判断是不是回文串。最后将链表的链表还原

【我去,这样太复杂了吧。没办法单链表只有一个指向后继结点的指针】

bool isPalidrome3(Node head){
    if(head == NULL){
        return true;
    }
    if(head->next == NULL){  
        return true;
    }
    Node slow = head;
    Node fast = head;
    while (fast->next != NULL && fast->next->next != NULL)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    Node n2 = fast;
    Node n1 = slow;
    n2 = n1->next;
    n1->next = NULL;
    Node n3 = NULL;
    while (n2 != NULL)
    {
        n3 = n2->next;
        n2->next = n1;
        n1 = n2;
        n2 = n3;
    }
    n3 = n1;
    n2 = head;
    bool res = true;
    while (n1 != NULL && n2 !=NULL){
        if(n1->value != n2->value){
            res = false;
            break;
        }
        n1 = n1->next;
        n2 = n2->next; 
    }
   n1 = n3->next;
   n3->next = NULL;
   while (n1 != NULL)
   {
       n2 = n1->next;
       n1->next = n3;
       n3 = n1;
       n1 = n2;
   }
    return res;
}

今日份试题2:

问题描述:将单向链表按值划分成左边小、中间相等、右边大的形式

实现思路1:把链表放入数组里,在数组上做partition

//将单个链表按某值划分成左边小、中间相等、右边达的形式
//code1-->把链表放入数组里,在数组上做化分 空间复杂度O(n)
void arrPartition(Node arr,int len,int pivot){
     int less = -1;    //-1表示没有比pivot小的数
     int more = len;   //len表示没有比pivot大的数
     int L    = 0;     //通过L来遍历数组
     //分三种情况 arr[L]<pivot arr[L]>pivot arr[L]==povit
     while(L < more){
        if(arr[L].value < pivot){
            swap(arr[++less],arr[L++]);
        }else if(arr[L].value > pivot){
            swap(arr[L],arr[--more]);
        }else{
            L++;
        }
     }
}
Node listPartition1(Node head, int pivot){
    if(head == NULL){
        return head;
    }
    Node cur = head;
    int  len = 0;    //单链表的长度
    int  i   = 0;
    //计算单链表的长度
    while(cur != NULL){
        len++;
        cur = cur->next;
    } 
    cout<<"length:"<<len<<endl;
    //计算len的作用体现出来了,因为要在堆上分配内存
    //如果我们没有指定len,那么编译器将无法直到要给我们多大的空间
    //如果你所可以分配一个很大的空间,但剩余的空间将会被浪费掉
    Node nodeArr= new node[len];
    cur = head;
    //将单链表中的值依次放入数组中
    for(int i=0;i<len;i++){
        nodeArr[i] = *cur;
        cur = cur->next;   
       // cout<<"--->"<<nodeArr[i].value<<endl;
    }
    //在数组上做划分
    arrPartition(nodeArr,len,pivot);
    //这里有两种处理方法
    /*1.以这个数组重新制作一个链表
      2.将这个数组中的值去覆盖原来链表中的值
    */
    //code1:
    // for(int i=1;i<len;i++){
    //     nodeArr[i-1].next = &nodeArr[i];
    // }
    // nodeArr[i].next = NULL;
    // delList(head);
    // return nodeArr;
    //code2:
    cur = head;
    for(int i=0;i<len;i++){
        cur->value=nodeArr[i].value;
        cur=cur->next;
    }
    delete nodeArr;         //别忘了释放堆上的空间
    return head;
}

实现思路2:分成小、中、大三部分,再把各个部分之间串起来。

//将单个链表按某值划分成左边小、中间相等、右边达的形式
//code2-->通过6个状态指针来将原来的链表分成三个链表
/*小于pivot 等于pivot 大于pivot*/
Node listPartition2(Node head,int pivot){
    Node sH = NULL;   //保存小于链表的头部
    Node sT = NULL;   //保存大于链表的尾部
    Node eH = NULL;   //保存等于链表的头部
    Node eT = NULL;   //保存等于链表的尾部
    Node mH = NULL;    //保存大于链表的头部
    Node mT = NULL;    //保存大于链表的尾部
    Node next = NULL;   //保持下一个结点,很关键的
    while(head != NULL){
        next = head->next;  //将限一个结点通过next先保存下来
        head->next = NULL;
        //arr[L] < pivot
        if(head->value < pivot){
             if(sH == NULL){    //当小链表没有结点的时候
                 sH = head;
                 sT = head;
             }else{
                 sT->next = head;
                 sT = head;
             }
        }else if(head->value > pivot){
            if(mH == NULL){
                mH = head;
                mT = head;
            }else{
                mT->next = head;
                mT = head;
            }
        }else{
            if(eH == NULL){
                eH = head;
                eT = head;
            }else{
                eT->next = head;
                eT = head;
            }
        }
        head = next;
    }
    //将三个链表连接起来
    /*将小于区的尾部连接到等于区的头部*/
    //如果有小于区才能连
    if(sT != NULL){
        sT->next = eH;
        //如果没有等于区就将等于区的尾部合并小于区
        eT = eT==NULL?sT:eT;
    }
    //将小于等于区的尾部连接在大于区
    if(eT != NULL){  //eT==NULL:既没有小于区也没有大于区
        eT->next = mH;
    }
    sH = sH!=NULL ? sH:(sH!=NULL?eH:mH);
    return sH;
}

今日份试题3:

一种特殊的单链表结点描述如下

class Node{
    int        value;
    Node       next;
    Node       rand;
    Node(int val){value=val;}
}

rand指针是单链表结构中新增的指针,rand可能指向链表中的任意一个结点,也可能指向NULL。

给定一个由Node结点类型组成的无环单链表的头节点head,请实现一个方法来完成这个链表的复制,并返回新链表。

解题思路1:通过哈希表映射,来保持父节点(被拷贝的那个)和子结点(复制的)之间的对应关系。

Node copyListWithRand1(Node head){
    unordered_map<Node,Node> map;
    Node cur = head;
    while(cur != NULL){
        map.insert(pair<Node,Node>(cur,new node(cur->value)));
        cur = cur->next;
    }
    cur = head;
    while (cur != NULL)
    {
        map[cur]->next = map[cur->next];
        map[cur]->rand = map[cur->rand];
        cur = cur->next;
    }
    Node tmp = map[head];
    return tmp;
}

解题思路2:

将新结点先挂在原先结点的下一个结点(如此以来便可以找到原结点所对应的复制结点)  

     然后拷贝rand的指向

     分离-->拷贝后的结点和原结点

Node copyListWithRand2(Node head){
    if(head == NULL){
        return head;
    }
    //创建新结点并挂原先结点上
    Node next = NULL;
    Node cur  = head;
    while(cur != NULL){
        next = cur->next;
        cur->next = new node(cur->value);
        cur->next->next = next;
        cur = next;
    }
    //拷贝rand结点
    Node curCopy = NULL;
    cur = head;
    next = NULL;
    while(cur != NULL){
        next = cur->next->next;
        curCopy = cur->next;
        curCopy->rand = cur->rand!=NULL ? cur->rand->next : NULL;
        cur = next;
    }
    // //分离
    cur = head;
    Node res = cur->next;
    next = NULL;
    while(cur != NULL){
        next = cur->next->next;
        curCopy = cur->next;
        cur->next = next;
        curCopy->next = next!=NULL ? next->next:NULL;
        cur = next;
    }
    return res;
}

其它辅助代码:

#include <iostream>
#include <cstdlib>
#include <unordered_map>    //c++?????
using namespace std;
struct node
{
public:
    int  value;
    node *next;
    node *rand;
    node(int data){this->value = data;this->next=NULL;this->rand=NULL;};
    ~node(){};
};
typedef node* Node;
//
Node init(){
   //????????
   Node head = NULL;
   Node next = NULL;
   //Node cur  = NULL;
   head = new node(1);
   next = new node(2);
   head->next = next;
   next->rand = head;
   next = new node(3);
   head->next->next = next;
   head->rand = next;
   return head;
}
//
void printList(Node head){
    if(head == NULL){
        return;
    }
    Node cur = head;
    while(cur != NULL){
        cout<<cur->value<<" "<<endl;
        cur = cur->next;
    }
}
//释放
void delList(Node head){
    if(head == NULL){
        return;
    }
    Node p = head->next;
    while(p!=NULL){
        if(p->rand != NULL){
            p->rand = NULL;
        }
        delete head;
        head = p;
        p = p->next;
    }
    delete head;
    head = NULL;
}
int main()
{
    Node head = NULL;
    Node pre  = NULL;
    //初始化
    head = init();
   // pre  = copyListWithRand1(head);
    pre = copyListWithRand2(head);
    cout<<"head-->"<<endl;
    printList(head);
    cout<<endl;
    cout<<"pre-->"<<endl;
    printList(pre);
    cout<<endl;
    delList(head);
    delList(pre);
    system("pause");
    return 0;
}
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
MySQL慢查询优化、索引优化,是必知必备,大厂面试高频,本文深入详解,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验分享。
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
|
4天前
|
机器学习/深度学习 算法
基于改进遗传优化的BP神经网络金融序列预测算法matlab仿真
本项目基于改进遗传优化的BP神经网络进行金融序列预测,使用MATLAB2022A实现。通过对比BP神经网络、遗传优化BP神经网络及改进遗传优化BP神经网络,展示了三者的误差和预测曲线差异。核心程序结合遗传算法(GA)与BP神经网络,利用GA优化BP网络的初始权重和阈值,提高预测精度。GA通过选择、交叉、变异操作迭代优化,防止局部收敛,增强模型对金融市场复杂性和不确定性的适应能力。
117 80
|
1天前
|
机器学习/深度学习 算法 索引
单目标问题的烟花优化算法求解matlab仿真,对比PSO和GA
本项目使用FW烟花优化算法求解单目标问题,并在MATLAB2022A中实现仿真,对比PSO和GA的性能。核心代码展示了适应度计算、火花生成及位置约束等关键步骤。最终通过收敛曲线对比三种算法的优化效果。烟花优化算法模拟烟花爆炸过程,探索搜索空间,寻找全局最优解,适用于复杂非线性问题。PSO和GA则分别适合快速收敛和大解空间的问题。参数调整和算法特性分析显示了各自的优势与局限。
|
5天前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
19 6
|
4天前
|
并行计算 算法 安全
面试必问的多线程优化技巧与实战
多线程编程是现代软件开发中不可或缺的一部分,特别是在处理高并发场景和优化程序性能时。作为Java开发者,掌握多线程优化技巧不仅能够提升程序的执行效率,还能在面试中脱颖而出。本文将从多线程基础、线程与进程的区别、多线程的优势出发,深入探讨如何避免死锁与竞态条件、线程间的通信机制、线程池的使用优势、线程优化算法与数据结构的选择,以及硬件加速技术。通过多个Java示例,我们将揭示这些技术的底层原理与实现方法。
43 3
|
11天前
|
机器学习/深度学习 前端开发 算法
婚恋交友系统平台 相亲交友平台系统 婚恋交友系统APP 婚恋系统源码 婚恋交友平台开发流程 婚恋交友系统架构设计 婚恋交友系统前端/后端开发 婚恋交友系统匹配推荐算法优化
婚恋交友系统平台通过线上互动帮助单身男女找到合适伴侣,提供用户注册、个人资料填写、匹配推荐、实时聊天、社区互动等功能。开发流程包括需求分析、技术选型、系统架构设计、功能实现、测试优化和上线运维。匹配推荐算法优化是核心,通过用户行为数据分析和机器学习提高匹配准确性。
39 3
|
11天前
|
算法
PAI下面的gbdt、xgboost、ps-smart 算法如何优化?
设置gbdt 、xgboost等算法的样本和特征的采样率
28 2
|
25天前
|
算法
基于GA遗传算法的PID控制器参数优化matlab建模与仿真
本项目基于遗传算法(GA)优化PID控制器参数,通过空间状态方程构建控制对象,自定义GA的选择、交叉、变异过程,以提高PID控制性能。与使用通用GA工具箱相比,此方法更灵活、针对性强。MATLAB2022A环境下测试,展示了GA优化前后PID控制效果的显著差异。核心代码实现了遗传算法的迭代优化过程,最终通过适应度函数评估并选择了最优PID参数,显著提升了系统响应速度和稳定性。
108 15
|
29天前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
22天前
|
算法
基于WOA鲸鱼优化的购售电收益与风险评估算法matlab仿真
本研究提出了一种基于鲸鱼优化算法(WOA)的购售电收益与风险评估算法。通过将售电公司购售电收益风险计算公式作为WOA的目标函数,经过迭代优化计算出最优购电策略。实验结果表明,在迭代次数超过10次后,风险价值收益优化值达到1715.1万元的最大值。WOA还确定了中长期市场、现货市场及可再生能源等不同市场的最优购电量,验证了算法的有效性。核心程序使用MATLAB2022a实现,通过多次迭代优化,实现了售电公司收益最大化和风险最小化的目标。

热门文章

最新文章