3、设L为带头节点的单链表,编写算法实现从尾到头反向输出每个节点的值。
我想到的是reverse一下/笑哭
想想这个就有点杀鸡用牛刀的感觉…
看了题解说是可以用递归…卧槽瞬间orz
class Solution{
public:
//利用递归解决
void reverseOut(ListNode* head){
if(head->next!=NULL)
reverseOut(head->next);
printf("%d ",head->val);
}
//利用reverse将整个链表倒置
void reverseOutput(ListNode *head){
ListNode root(-1);
root.next=head;
//获得尾节点
ListNode* end=head;
while(end->next!=NULL) end=end->next;
//整个链表反转过来
reverse(&root,head,end);
outputNode(head);
}
ListNode* reverse(ListNode* prev,ListNode* begin,ListNode *end){
ListNode* end_next=end->next;
for(ListNode *p=prev,*cur=begin,*next=cur->next;
cur!=end_next;
p=cur,cur=next,next=next==NULL?NULL:next->next) {
cur->next=p;
}
begin->next=end_next;
prev->next=end;
return begin;
}
};
8、给定两个单链表,找出两个链表的公共节点。
这道题我想到的办法是用哈希,以unordered_map对指针进行存储,这样遍历第一个链表。
再对第二个链表进行遍历,并判断每个指针在里面是否存在。
如果存在,推入一个vector里面
讲道理效率应该还不错O(n+m),如果不允许用额外存储空间呢?
我们可以确信,二者相遇之后,肯定是相同的节点一直到end,为什么呢?
因为链表只有唯一的next域
于是我们可以算出两个链表路程差距是多少,然后让其中一头先走这么多个差距
然后再一起向前走,ok那样就会同时到达共同节点
复杂度O(m+n)
class Solution{
public:
//技巧
ListNode* getSameNodes(ListNode* head1,ListNode* head2) {
int len1=0,len2=0;
for(ListNode* temp=head1;temp;temp=temp->next) len1++;
for(ListNode* temp=head2;temp;temp=temp->next) len2++;
//获取到两个长度之后,要取得差值,并将差值对应的项目向前移动xxx
ListNode* cur1,*cur2;
if(len1>len2){
//先走k步,让二者可以在同一点相遇
int k=len1-len2;
while(k--) len1=len1->next;
}
else{
int k=len2-len1;
while(k--) len2=len2->next;
}
while(len1){
if(len1==len2) return len1;
else{
len1=len1->next;
len2=len2->next;
}
}
return null;
}
//使用哈希
vector<ListNode*> getSameNodes(ListNode* head1,ListNode* head2){
vector<ListNode*> result;
unordered_map<ListNode*,bool> existed;
for(ListNode* temp=head1;temp!=NULL;temp=temp->next) {
existed[temp]=true;
}
for(ListNode* temp=head2;temp!=NULL;temp=temp->next) {
if(existed.find(temp)!=existed.end()){
result.push_back(temp);
}
}
return result;
}
};
13、合并两个递增的链表,要求合并之后的链表递减。
这道题很显然是归并的思路,然后借鉴了LeetCode上 add two number的思路,
使判断结束条件变为cur1|| cur2.这样在一个链表长度大于另一个的时候,使用辅助变量max来帮助计算。
从而简化代码
class Solution{
public:
//将两个递增链表合并,要求合并后链表递减
ListNode* mergeReverse(ListNode* head1,ListNode* head2) {
ListNode root(-1),root1(-1),root2(-1);
ListNode* head=&root;
int max=999999;
for(ListNode* prev1=&root1,*prev2=&root2,*cur1=head1,*cur2=head2;
cur1 || cur2;){
int v1=cur1==NULL?max:cur1->val;
int v2=cur2==NULL?max:cur2->val;
if(v1<v2) {
//取head1链表
ListNode* next=cur1->next;
cur1->next=head->next;
head->next=cur1;
cur1=next;
}
else{
//取head2
ListNode* next=cur2->next;
cur2->next=head->next;
head->next=cur2;
cur2=next;
}
}
return root.next;
}
};
16、判断一个序列是否是另一序列的子序列
用的很朴素的办法,不过还行
class Solution{
public:
//判断head2链表是否是head1链表的子链
bool IsSubSequence(ListNode* head1,ListNode* head2) {
for(ListNode *cur1=head1;cur1;cur1=cur1->next){
int v1=cur1->val;
ListNode *cur2=head2,*temp=cur1;
while(temp && cur2 && temp->val==cur2->val){
temp=temp->next;cur2=cur2->next;
}
//没有找到
if(cur2!=NULL) continue;
return true;
}
return false;
}
};
17、判断循环双链表是否对称
算法没有什么好说的,有些地方需要关心一下,具体详见注释
主要有:1、struct定义
2、堆栈变量和内存变量的区别
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<vector>
using namespace std;
struct ListNode {
int val;
ListNode *prior,*next;
//这里是c++的一种初始化方法,利用 变量名(变量值) 来进行初始化
//在{}内再进行相关的操作,即初始化与操作分离
ListNode(int x) : val(x),prior(NULL),next(NULL) {}
};
ListNode* initNode(vector<int> arr){
//注意这里的root
//如果申明为 ListNode root(-1),这里的root将是一个堆栈变量
//在函数执行结束之后,将会被释放
//于是将一个堆栈变量的指针传回,它将变成一个无意义的指针
//所以需要使用new,将其申明为内存变量
ListNode* root=new ListNode(-1);
ListNode* end = root;
int n = arr.size();
for (int i = 0; i < n; i++){
end->next = new ListNode(arr[i]);
end->next->prior=end;
end = end->next;
}
end->next=root;
root->prior=end;
return root;
}
void outNode(ListNode* root){
//带头节点的双链表
ListNode* head=root->next;
while(head && head!=root){
printf("%d ",head->val);
head=head->next;
}
printf("\n");
}
class Solution{
public:
//root是头节点
bool IsSymmetry(ListNode *root){
for(ListNode* p=root->prior,*q=root->next;p!=q;p=p->prior,q=q->next){
if(p->val!=q->val){
return false;
}
}
return true;
}
};
19、双链表元素删除x
链表的删除有一点需要注意,如果需要删除x元素,不需要遍历两遍。
只需要在第一遍遍历的时候使用双指针,另一个指针叫做minp,保存最小节点的前驱节点。
这样在一轮循环之后,可以在O(1)的时间内,利用这个前驱删除相应的节点
21、找出倒数第k个元素
使用两个指针,第一个指针先走k步,然后两个指针一起走,直到第一个指针走到末尾。
然后就是在向前走k的时候注意指针判空这些小细节
class Solution{
public:
int get_kth_back(ListNode* head,int k){
ListNode* pre=head,*cur=head;
for(int i=1;i<k && cur;i++) cur=cur->next;
if(!cur) return -1;
for(;cur->next;cur=cur->next,pre=pre->next);
if(pre) return pre->val;
else return -1;
}
};
23、删除重复节点
没什么好说的,主要想说一下unordered_map头文件引用
一般应该#include< unordered_map >就行了,但是我的devc不过
于是,应当是
#if(__cplusplus == 201103L)
#include <unordered_map>
#include <unordered_set>
#else
#include <tr1/unordered_map>
#include <tr1/unordered_set>
namespace std
{
using std::tr1::unordered_map;
using std::tr1::unordered_set;
}
#endif
思考题:找出一个数组里大小之和为k的
重新回顾一下快排
int Partition(int a[],int left,int right){
int x=a[left];
while(left<right){
while(left<right && a[right]>x) right--;
if(left<right) a[left]=a[right];
while(left<right && a[left]<x) left++;
if(left<right) a[right]=a[left];
}
a[left]=x;
return left;
}
void QSort(int a[],int left,int right){
if(left<right){
int mid=Partition(a,left,right);
QSort(a,0,mid-1);
QSort(a,mid+1,right);
}
}