描述
输入一个单向链表,输出该链表中倒数第k个结点,链表的倒数第1个结点为链表的尾指针。
链表结点定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
正常返回倒数第k个结点指针,异常返回空指针.
要求:
(1)正序构建链表;
(2)构建后要忘记链表长度。
数据范围:链表长度满足 1 \le n \le 1000 \1≤n≤1000 , k \le n *k≤n* ,链表中数据满足 0 \le val \le 10000 \0≤val≤10000
本题有多组样例输入。
输入描述:
输入说明 1 输入链表结点个数 2 输入链表的值 3 输入k的值
输出描述:
输出一个整数
示例1
输入:
8
1 2 3 4 5 6 7 8
4
复制
输出:
5
具体思路
-
法1:快慢指针法,设置快指针和慢指针,快指针比满指针快k步,当快指针遍历到尾结点的时候,满指针得位置便是倒数第k个结点
-
法2:头插建表法,对于逆向输出的数据,都可以采用头插法建立新表的方式逆向输出
-
法3:对于逆序的题目,都可以使用递归法
-
具体实现
#include <math.h>
#include <algorithm>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <stdlib.h>
#include <cstdlib>
#include <cstring>
//#include "./include/list/sqlist.h"
using namespace std;
typedef struct ListNode{
int data;
struct ListNode *next;
/* data */
}ListNode, *LinkList;
//day6
//王道第21题
//蓝蓝星球打卡题目,输入一个单向链表,输出该链表中倒数第k个结点,链表的倒数第1个结点为链表的尾指针。
//尾插法建立链表
/**
* @description:
* @param {LinkList} &L
* @param {int} n
* @return {*}
*/
LinkList ListTailInsert(LinkList &L, int n){
L = (LinkList)malloc(sizeof(ListNode));//创建头结点
ListNode* s;//创建临时结点
ListNode* r = L;//创建尾结点
int x;
//L->next = NULL;
//scanf("%d",&x);
while(n--){
scanf("%d",&x);
s=(ListNode*)malloc(sizeof(ListNode));
s->data = x;
r->next = s;
r = s;
//scanf("%d",&x);
}
r->next = NULL;
return L;
}
//打印单链表
void PrintLinkedList(LinkList& L){
ListNode* p = L->next;//头结点
while(p!=NULL){
std::cout << p->data << "--->" << "\a";
p = p->next;//下个结点
}
std::cout << std::endl;
}
//法2:设置p,q两个工作指针,p为快指针,先移动k位,然后再同步移动q指针,当p到尾部时,q处于的位置是倒数第k个
/**
* @description: 时间复杂度O(n),空间复杂度O(1)
* @param {LinkList &} L
* @param {int} k
* @return {*}
*/
ListNode* FindKInListII(LinkList & L, int k ){
//第一步设置快慢指针
ListNode *p = L->next, *q = L->next;
int count = 0;//计数,当count==k的时候p才移动
while(q != NULL){
if(count < k) count++;
else p = p->next;
q = q->next;
}
if(count < k) return NULL;
else{
return p;
}
}
//法1:头插建表法
/**
* @description: 时间复杂度O(n) 空间复杂度O(n)
* @param {LinkList} L
* @param {int} k
* @return {*}
*/
ListNode* FindKInList(LinkList L,int k){
LinkList L1 = (LinkList) malloc(sizeof(ListNode));
//L1 = L;//将L的头结点赋给L1,作为L1的新头结点
//L1->next = NULL;//新链表
ListNode *p = L->next, *q;//工作结点和临时结点
L->next = NULL;
//使用头插法依次插入到L1
while(p != NULL){
//q = (ListNode*) malloc(sizeof(ListNode));
//q = p;//先赋值
q = p->next;
p->next = L->next;
L->next = p;
p = q;//继续遍历
}
//第二部分就是在新链表中直接查找第k个元素
ListNode* p1 = L->next;//新链表的元素
//PrintLinkedList(p1);
while(p1 != NULL && k != 0){
q = p1;
p1 = p1->next;
k--;
}
if(k != 0) return NULL;
else{
return q;
}
}
//法3:递归法
/**
* @description: 时间复杂度O(n),空间复杂度O(n)额外的栈空间
* @param {LinkList} L
* @param {int &} index
* @return {*}
*/
ListNode* FindKInListIII(LinkList L, int & index){
//终止条件
if(L == NULL) return NULL;
//做点什么
//先遍历下一个
ListNode *p = FindKInListIII(L->next, index);
if(--index == 0) return L;
else return p;
}
int main(){
//day06
LinkList L;
int n;//输入数据数量
int index = 0;//输入的位置
ListNode* p;
while(scanf("%d",&n) != EOF){
L = ListTailInsert(L,n);
scanf("%d",&index);
if(index == 0) std::cout << 0 << std::endl;
else{
p = FindKInListIII(L,index);
if(p != NULL) printf("%d\n",p->data);
}
//PrintLinkedList(L);
}
system("pause");
return 0;
}
时间复杂度
- 法1:时间复杂度:O(n),空间复杂度:O(1)
- 法2:时间复杂度:O(n),空间复杂度:O(n)
- 法3:时间复杂度:O(n),空间复杂度:O(N)
小结
对比于这类题目,法2头插建表法属于暴力破解法,如果想不到快慢指针法可以采用这类做法
法1才是最优解,空间复杂度为O(1)