1 c c++指针的使用方法在这道题中有很好体现,不仅有指针还有指针的指针,即LinkNode **root.
规则,如下:
1. 可以把变量,指针,指针的指针当做三层,*号是退层,&是进层(&主要用在传参数的时候)。这样可以把所有有关的指针变量转换为 LinkNode LinkNode* LinkNode** 这三类。
2. 这三类中,不论形式参数是**root, 还是*root,还是root,实参都无法在子函数中改变,只有形参才会变化。
3.形参和实参形式的确定: 如果想用指针,不论是哪种类型,*root 还是**root,主函数中的实参一般都定义为*root,定义为**root会不断报错。而形式参数一般根据是否需要改变指针来确定是否需要用**root,和主函数的实参形式无关。
4. 如果使用*root做参数时发现需要对root进行修改,那么就需要用到**root了。
5. 指针运算符->与*具有同样的作用。
6. *运算符的运算级别比->弱,所以需要加上(),即(*root)->value = 7;
7. 可以结合这个题的代码来理解以上规则。
具体题目见下面:
https://www.nowcoder.com/question/next?pid=11727202&qid=218378&tid=20369002
题目描述
翻转一个环形的链表,下面给出LinkNode数据结构和需要实现的函数。(环形链表没有头指针,即空链表用NULL/null表示)。
// C/C++
struct LinkNode {
int value;
LinkNode * next
};
void reverse(LinkNode * root) {
// TODO
}
// Java
public class LinkNode {
private int value;
private LinkNode next;
}
public void reverse(LinkNode root) {
// TODO
}
我的思路:一种方法是借助vector数组来翻转,另外一种方法是就地逆置,即空间复杂度为O(1),方法是把节点都摘下来,然后按照头插法再重建一次,由于要用到头插法,所以初始化的时候就新建了头指针,然后把第一个节点当做头结点来做具体可以见我的图示。两种方法的时间复杂度都是O(n).
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include <map>
#include <vector>
using namespace std;
struct LinkNode {
int value;
LinkNode* next;
};
void InitList(LinkNode **L) {
*L = (LinkNode*)malloc(sizeof(LinkNode));//malloc的返回参数是指针类型的,应该用指针类型对其强制转化。
(*L)->next = *L;
return;
}
void CreatList(LinkNode* root) {
LinkNode *p, *s;
p = root;//尾插法,把p当做尾节点
int tp = 0;
scanf("%d", &p->value);
while (scanf("%d",&tp) != EOF) {
s = (LinkNode*)malloc(sizeof(LinkNode));
s->value = tp;
p->next = s;
p = s;
}
s->next = root;
return;
}
void ListReverse(LinkNode *root) {
vector<int> v;
LinkNode *p = root->next;
v.push_back(root->value);
while (p != root) {
v.push_back(p->value);
p = p->next;
}
for (int i = 0;i < v.size();i++) {
p->value = v[v.size() - i -1];
p = p->next;
}
return;
}
void ListReverse1(LinkNode **root) {
LinkNode *root1 = (LinkNode*)malloc(sizeof(LinkNode));
root1->next = *root;
LinkNode *tp = (*root)->next;
LinkNode *p = tp->next;//显然*和->具有同样的作用,即*p和->next 是同一种类型的变量。
(*root)->next = NULL;
do {
tp->next = root1->next;
root1->next = tp;
tp = p;
p = p->next;
} while (tp != *root);
(*root)->next= root1->next;//显然*root和root1才是同一种类型的。
*root = root1->next;/*从主函数传参到子函数,无论将形参声明为*,还是** 都无法对指针变量进行改变。
显然,指针运算符->和*的功能很相近,这也是虽然无法对指针变量修改,但却可以对next域进行修改的原因。*/
return;
}
void ListReverse2(LinkNode *root) {
root = root->next;
return;
}
void PrintList(LinkNode *root) {
LinkNode *p = root;
do {
printf("%d ",p->value );
p = p->next;
} while (p != root);
return;
}
void change(int *a) {
int *c = (int *)malloc(sizeof(int));
*c = 5;
a = c;
}
int main()
{
freopen("Text.txt", "r", stdin);
LinkNode* root;//不要定义为LinkNode** root,无论怎么传参,或者在主函数中初始化都会在进入InitList时报错
InitList(&root);
CreatList(root);
PrintList(root);
ListReverse1(&root);
//ListReverse2(*root);
PrintList(root);
return 0;
}
第二种方法的图解: