上次商汤科技面试问到删除链表倒数第N个节点的题目,题目本身不难,但是需要自己写链表的输入,结构体创建等,下面附上两种方法,其实是一样的哈哈,细微区别,考虑到了链表可能输入长度是0的情况!
Case1:自定义输入链表元素,然后实现功能,链表元素可以不输入就是空链表
代码1:val赋值和链表创建分开:
//删除倒数第N个链表j节点自己写
// head->next=new Listnode();这样是不行的,因为next是空节点,没有资格进行操作,因为没有val
#include<bits/stdc++.h>
using namespace std;
struct ListNode{
int val;
ListNode*next;
ListNode():val(0),next(nullptr){}
ListNode(int x):val(x),next(nullptr){}
};
ListNode* DeleteNode(ListNode* head,int n); //函数声明
int main(){
cout<<"Please enter the length of the list: ";
int M; cin>>M;
vector<int>nums(M);
ListNode* head=new ListNode();
ListNode* temp=head;
if(M==0){
cout<<"nullptr";
return 0;
}
for(int i=0;i<M;i++){
cin>>nums[i];
temp->next=new ListNode(nums[i]); //反正注意把,只有new的元素可以对next进行操作,操作也是给new!! 除非索引到已经建立的链表就Listnode*temp=head;
temp=temp->next;
temp->next=nullptr;//稳妥起见
}
head=head->next;
//调用函数
cout<<"Please delete the last n node: ";
int n; cin>>n;
ListNode* Head=DeleteNode(head,n);
if(Head==nullptr)
cout<<"This is nullptr";
while(Head!=nullptr){
cout<<Head->val<<" ";
Head=Head->next;
}
return 0;
}
///leetcode函数
ListNode* DeleteNode(ListNode* head,int n){
if(head==nullptr)
return head;
ListNode* dummy = new ListNode(0);
dummy->next=head;
ListNode* cur = dummy;
int length=0;
while(head){
length++;
head=head->next;
}
for (int i = 1; i < length - n + 1; ++i) {
cur = cur->next;
}
cur->next = cur->next->next;
ListNode* ans = dummy->next;
delete dummy;
return ans;
}
代码2:val和链表创建合在一起:
//删除倒数第N个链表j节点自己写
// head->next=new Listnode();这样是不行的,因为next是空节点,没有资格进行操作,因为没有val
#include<bits/stdc++.h>
using namespace std;
struct ListNode{
int val;
ListNode*next;
ListNode():val(0),next(nullptr){}
ListNode(int x):val(x),next(nullptr){}
};
ListNode* DeleteNode(ListNode* head,int n); //函数声明
int main(){
cout<<"Please enter the length of the list: ";
int M; cin>>M;
vector<int>nums(M);
ListNode* head=new ListNode(); //没有赋值就是空结点
ListNode* temp=head;
for(int i=0;i<M;i++){
cin>>nums[i];
temp->val=nums[i];
if(i!=M-1){
temp->next=new ListNode(); //反正注意把,只有new的元素可以对next进行操作,操作也是给new!! 除非索引到已经建立的链表就Listnode*temp=head; 链表这种必须分配空间就是反正
temp=temp->next;
}
temp->next=nullptr;//稳妥起见
}
//调用函数
cout<<"Please delete the last n node: ";
int n; cin>>n;
ListNode* Head=DeleteNode(head,n);
if(Head==nullptr)
cout<<"This is nullptr";
while(Head!=nullptr){
cout<<Head->val<<" ";
Head=Head->next;
}
return 0;
}
///leetcode函数
ListNode* DeleteNode(ListNode* head,int n){
if(head==nullptr)
return head;
ListNode* dummy = new ListNode(0);
dummy->next=head;
ListNode* cur = dummy;
int length=0;
while(head){
length++;
head=head->next;
}
for (int i = 1; i < length - n + 1; ++i) {
cur = cur->next;
}
cur->next = cur->next->next;
ListNode* ans = dummy->next;
delete dummy;
return ans;
}
下面附上删除正数第N个节点的主干程序:
如输入1->2->3->3->4->5,删除第3个节点;
ListNode * deleteNodes(ListNode* head,int& index)
{
if(head== nullptr)
{
return nullptr;
}
ListNode* dumpy=new ListNode();
dumpy->next=head;
ListNode* cur=head;
ListNode* prev=dumpy;
int count=1;
while(cur!= nullptr)
{
if(count==index)
{
prev->next=cur->next;
break;
}
prev=cur;
cur=cur->next;
count++;
}
return dumpy->next;
}
————————————————————————————————————————
Case2:根据输入的数组来创建二叉树,然后对二叉树操作Leetcode题目实现功能:
除了链表,二叉树的输入也是题目可能会考到的,下面举个简单的二叉树问题来分析代码:
代码1: 层序遍历输入数组创建二叉树,层序遍历输出,这里输出可以前中后序就不展开了。这里的情况针对输入是完全二叉树
//程序功能:输入一组数据是二叉树的层序遍历,要求根据数据创建二叉树,然后用层序遍历来输出二叉树
#include<string>
#include<sstream>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
TreeNode():val(0),left(NULL),right(NULL){} //这样允许我们可以使用 new TreeNode 或者 new TreeNode()
TreeNode(int x):val(x),left(NULL),right(NULL){} //这里就简单这么赋值把
};
TreeNode* creatBinTree(vector<int> & arr) ;
void printBinTree_bfs(TreeNode* head); //声明下二叉树输出函数
int Final_Task(TreeNode* head);
int main() {
vector<int> a;
cout<<"Please enter the numbers: "<<endl;
string str; getline(cin,str);
stringstream ss(str); string temp;
while(getline(ss,temp,' '))
{
a.push_back(stoi(temp));
}
TreeNode* head = creatBinTree(a); //注意到这样就行了,虽然里面是创建了形参,但是我最终给他return出来了两个地址对上了地址的改变还是存在的!!!
printBinTree_bfs(head);
//int res=Final_Task(head); //比如是int形式的输出
//cout<<res;
}
//按照层序遍历构造二叉树--二叉树构造程序
TreeNode* creatBinTree(vector<int> & arr) {
queue<TreeNode*> q;
//如果层序序列为空,返回空树
if (arr.empty()) {
return nullptr;
}
TreeNode* head =new TreeNode; //创建头节点 注意到结构体的赋值用new一般是针对指针式样结构体 如链表和树这种
head->val = arr[0]; //存放数组首元素
q.push(head); //入队
TreeNode* temp; //工具人temp
int i = 1;
while (!q.empty()) {
temp = q.front(); //取出头节点,准备给它安排左右孩子
q.pop(); //头节点出队,每一次新的循环,都让头出队
//先弄左孩子
//i只要不超过数组的有效长度,就有左孩子从0开始计数,0已经赋值就1了
if (i < arr.size()) {
temp->left = new TreeNode(); //或者 new TreeNode
temp->left->val = arr[i];
q.push(temp->left); //左孩子入队
i++; //数组后移
}
else {
temp->left = nullptr;
}
//再弄右孩子
if (i < arr.size()) {
temp->right = new TreeNode();
temp->right->val = arr[i];
q.push(temp->right); //右孩子入队
i++; //数组后移
}
else {
temp->right = nullptr;
}
}
return head; //最后队列为空就出while,返回头节点
}
//层序遍历二叉树
void printBinTree_bfs(TreeNode* head) {
queue<TreeNode*> q;
//树为空
if (head == nullptr) {
cout << "TreeNode is empty!" <<endl;
return;
}
//头节点入队
q.push(head);
TreeNode* b; //工具人b
while (!q.empty()) {
b = q.front(); //拿到队头,队头出队
q.pop();
cout <<b->val<< endl; //打印对头的数据
//对头的左孩子存在就入队
if (b->left) {
q.push(b->left);
}
//对头的右孩子存在就入队
if (b->right) {
q.push(b->right);
}
}
}
///自己的程序
/*
Final_Task(TreeNode*head){
//比如输出二叉树的最大深度
}
*/
代码2:那有人会问,上面的情况是 针对完全二叉树,如果输入的数据中有null怎么办啊 可以在输入时候做微小的改动,这里是层序遍历的输入元素,可以存在nullptr,然后层序输出二叉树元素
//程序功能:输入一组数据是二叉树的层序遍历,要求根据数据创建二叉树,然后用层序遍历来输出二叉树,支持输入的元素是逗号隔开存在nullptr
#include<string>
#include<cstring>
#include<sstream>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
TreeNode():val(0),left(NULL),right(NULL){} //这样允许我们可以使用 new TreeNode 或者 new TreeNode()
TreeNode(int x):val(x),left(NULL),right(NULL){} //这里就简单这么赋值把
};
TreeNode* creatBinTree(vector<string> & arr) ;
void printBinTree_bfs(TreeNode* head); //声明下二叉树输出函数
int Final_Task(TreeNode* head);
int main() {
vector<string> a;
cout<<"Please enter the numbers: "<<endl;
string str; getline(cin,str);
stringstream ss(str); string temp;
while(getline(ss,temp,' '))
{
a.push_back(temp);
}
TreeNode* head = creatBinTree(a); //注意到这样就行了,虽然里面是创建了形参,但是我最终给他return出来了两个地址对上了地址的改变还是存在的!!!
printBinTree_bfs(head);
//int res=Final_Task(head); //比如是int形式的输出
//cout<<res;
}
//子程序1:-------------------------按照层序遍历构造二叉树--二叉树构造程序
TreeNode* creatBinTree(vector<string> & arr) {
queue<TreeNode*> q;
if (arr.empty()) {
return nullptr;
}
TreeNode* head =new TreeNode; //创建头节点 注意到结构体的赋值用new一般是针对指针式样结构体 如链表和树这种
head->val = stoi(arr[0]);
q.push(head);
TreeNode* temp; //工具人temp
int i = 1;
while (!q.empty()) {
temp = q.front();
q.pop();
//先弄左孩子
//i只要不超过数组的有效长度,就有左孩子从0开始计数,0已经赋值就1了
if (i < arr.size()) {
if(arr[i]=="nullptr")
temp->left=nullptr;
else{
temp->left = new TreeNode(); //或者 new TreeNode
temp->left->val = stoi(arr[i]);
q.push(temp->left); //左孩子入队
}
i++; //数组后移
}
else
temp->left = nullptr;
//再弄右孩子
if (i < arr.size()) {
if(arr[i]=="nullptr")
temp->right=nullptr;
else{
temp->right = new TreeNode();
temp->right->val = stoi(arr[i]);
q.push(temp->right); //右孩子入队
}
i++; //数组后移
}
else
temp->right = nullptr;
}
return head; //最后队列为空就出while,返回头节点
}
//子程序2:------------------------层序遍历输出二叉树
void printBinTree_bfs(TreeNode* head) {
queue<TreeNode*> q;
//树为空
if (head == nullptr) {
cout << "TreeNode is empty!" <<endl;
return;
}
//头节点入队
q.push(head);
TreeNode* b; //工具人b
while (!q.empty()) {
b = q.front(); //拿到队头,队头出队
q.pop();
cout <<b->val<< endl; //打印对头的数据
//对头的左孩子存在就入队
if (b->left) {
q.push(b->left);
}
//对头的右孩子存在就入队
if (b->right) {
q.push(b->right);
}
}
}
///自己的程序
/*
Final_Task(TreeNode*head){
//比如输出二叉树的最大深度
}
*/
————————————————————————————————————————
Case3:
有时候我们的输入不是层序遍历,比如是前序遍历,或者后序中序遍历。这里原理是一样的。我们取前序遍历来展开。即,输入的元素比如1 2 3 4是二叉树的前序遍历顺序,要求我们创建对应二叉树实现功能,如何创建二叉树呢:
下面的是前序遍历构建二叉树的子程序! 注意到我们的index作为全局变量初始值给1,因为遇到空节点的时候会自动return,这样就不会说出不来了。注意就是当我们是right或者left指向的时候其实宏观是这个根节点的位置!!这个是递归的想法
//子程序1:-------------------------按照前序构造二叉树--二叉树构造程序
TreeNode* creatBinTree(vector<string> & arr) {
if(arr.size()==0)
return nullptr;
string value=arr[index];
index++;
if (value =="nullptr") {
return nullptr;
}
TreeNode* head = new TreeNode(stoi(value));//如果不为空则创建该节点,同时递归进行左节点与右节点的创建
head->left = creatBinTree(arr);
head->right= creatBinTree(arr);
return head;
}
或者,如何实现后续遍历输入呢:
对于1 2 3然后下面4个空节点的二叉树,我们按照后序遍历输入:
X X 2 X X 3 1,层序遍历输出应该是1 2 3
下面附上代码,其思路是按照根 右 左,和后续遍历的顺序相反来构建二叉树,前提是对输入数组进行一个反转。这样就能和前序遍历类似来构造函数了:
这也是考虑到了中序或者后续遍历开头就是nullptr,不好返回
//子程序1:-------------------------按照后续遍历构造二叉树--二叉树构造程序
TreeNode* creatBinTree(vector<string> & arr) {
if(arr.size()==0)
return nullptr;
string value=arr[index];
index++;
if (value =="nullptr") {
return nullptr;
}
TreeNode* head = new TreeNode(stoi(value));
head->right= creatBinTree(arr); //注意到我这边用了 根、右、左的顺序。输出的是相反的
head->left = creatBinTree(arr);
return head;
}
中序遍历不能得到有效解:
可以看到同样的输入序列有不同的二叉树~