排序链表-移动节点还是交换数据成员
Sorting Linked Lists - move nodes or swap data members?
我有一个简单的问题。我正在开发一个C++应用程序,它是一个联系人列表应用程序。它存储多个人的姓名、地址、号码、年龄等。我使用的是灰泥和链表(节点)。我正在构建排序列表函数,以按字母顺序排列列表。我目前想知道,通过整体移动结构或交换每个节点内的数据成员来重新排序列表是否更好。起初,我考虑移动节点,但现在交换数据成员似乎更安全,因为我不必重新排序列表。无论如何,我不知道其中一方是否比另一方有任何好处。
编辑:这是我正在编写的源代码。请注意排序函数是不完整的。此外,我还是一个新手程序员,所以从专业的角度来看,编码可能会有很多问题。除此之外,我还没有完成它。我写它只是为了在暑假编程课间隙练习编码。
#include <iostream>
#include <fstream>
#include <string.h>//for functions in date function
#include <time.h> //for functions in date function
#include <sys/stat.h>//for mkdir functions
#include <unistd.h>//for usleep function
#include <ctype.h>//for toupper function in swap function
using namespace std;
struct PersonInfo
{
char FirstName[20];
char LastName[20];
char Address[40];
char PhoneNumber[20];
int Age;
PersonInfo *Link;
};
bool EmptyFileChecker(ifstream &FI, const char *P);
void AddPeopleToList(PersonInfo *&HeadPointer);
void RebuildOldList(ifstream &FI, PersonInfo *&HeadPointer, const char *P);
void DisplayList(PersonInfo *HeadPointer);
void SaveSettings(ofstream &FO, const PersonInfo *HeadPointer, const char *P);
void DisplayMenu(PersonInfo *&HeadPointer, const char *P, ifstream &FileIn, ofstream &FileOut);
void SortContacts(PersonInfo *&HeadPointer);
bool NamesInOrder(const char LastName1[], const char LastName2[]);
string Date();
//Delete Contact
//ENCRYPT LIST?
//Check for memory leaks in code and destructor?
//Return something - noun-like
//void adjective - like
int main()
{
PersonInfo *HeadPointer;
const char *Path = "/Users/josephlyons/Library/Application Support/The Lyons' Den Labs/TheLyons'DenContactInformation.txt";//code variable for username
ifstream FileIn;
ofstream FileOut;
mkdir("/Users/josephlyons/Library/Application Support/The Lyons' Den Labs", ACCESSPERMS);//MODE??
if (!EmptyFileChecker(FileIn, Path))
AddPeopleToList(HeadPointer);
else
RebuildOldList(FileIn, HeadPointer, Path);
DisplayMenu(HeadPointer, Path, FileIn, FileOut);
//SortContacts(HeadPointer);
SaveSettings(FileOut, HeadPointer, Path);
}
void DisplayMenu(PersonInfo *&HeadPointer, const char *P, ifstream &FileIn, ofstream &FileOut)
{
short int MenuChoice;
do
{
cout << "(1) Display Contact Listn";
cout << "(2) Organize Contact Listn";//delete when done with program and automatically sort list before saving.
cout << "(3) Add Contact/sn";
cout << "(4) Delete Contact/sn";
cout << "(5) Quitnn";
cout << "Choice: ";
cin >> MenuChoice;
if (MenuChoice == 1)
DisplayList(HeadPointer);
else if (MenuChoice == 2)
SortContacts(HeadPointer);
else if (MenuChoice == 3)
AddPeopleToList(HeadPointer);
else if (MenuChoice == 4)
cout << "choice 4";
}
while(MenuChoice != 5);
}
bool EmptyFileChecker(ifstream &FI, const char *P)//DONE
{
FI.open(P);
if (FI.fail())
return false;
else if (FI.eof())//return 0 if file doesnt exist or if file is empty
return false;
else
return true;
}
void AddPeopleToList(PersonInfo *&HeadPointer)
{
PersonInfo *CurrentPosition;
char UserChoice;
do
{
CurrentPosition = new PersonInfo;
if (CurrentPosition == NULL)
{
cout << "Not enough memmory to make new contact.";
return;
}
cout << "nEnter First Name: ";
cin >> CurrentPosition->FirstName;
CurrentPosition->FirstName[0] = toupper(CurrentPosition->FirstName[0]);//automatically capitalize first name
cout << "Enter Last Name: ";
cin >> CurrentPosition->LastName;
CurrentPosition->LastName[0] = toupper(CurrentPosition->LastName[0]);//automatically capitalize last name
cin.ignore();//flushes a single newline left in input buffer from previous cin >>
cout << "Enter Adress: ";
cin.getline(CurrentPosition->Address, 40);//using cin.get() to allow for spaces in address
cout << "Enter Phone Number: ";
cin.getline (CurrentPosition->PhoneNumber, 20);//using cin.get() to allow for spaces in number
cout << "Enter Age: ";
cin >> CurrentPosition->Age;
cout << "nAdd another contact? Y/N: ";
cin >> UserChoice;
cout << "n";
CurrentPosition->Link = HeadPointer;
HeadPointer = CurrentPosition;
}
while (UserChoice == 'y' || UserChoice == 'Y');
SortContacts(HeadPointer);
}
void RebuildOldList(ifstream &FI, PersonInfo *&HeadPointer, const char *P)
{
PersonInfo *TemporaryPersonPointer;
char EndOfListChecker = 1;//initialized at a not 0 to allow entrance into loop
while (EndOfListChecker != 0)
{
TemporaryPersonPointer = new PersonInfo;
if (TemporaryPersonPointer == NULL)
cout << "Not enough memory to generate the full list";
FI >> TemporaryPersonPointer->FirstName;
FI >> TemporaryPersonPointer->LastName;
FI.ignore();//flushes a single newline from input
FI.getline(TemporaryPersonPointer->Address, 40);
FI.ignore();
FI.getline(TemporaryPersonPointer->PhoneNumber, 20);
FI >> TemporaryPersonPointer->Age;
TemporaryPersonPointer->Link = HeadPointer;
HeadPointer = TemporaryPersonPointer;
FI.get(EndOfListChecker);
while (EndOfListChecker == 'n')
{
FI.get(EndOfListChecker);
}
if (EndOfListChecker != 0)
FI.putback(EndOfListChecker);
}
}
void DisplayList(PersonInfo *HeadPointer)
{
do
{
cout << "nFirst Name: ";
cout << HeadPointer->FirstName << endl;
cout << "Last Name: ";
cout << HeadPointer->LastName << endl;
cout << "Adress: ";
cout << HeadPointer->Address << endl;
cout << "Phone Number: ";
cout << HeadPointer->PhoneNumber << endl;
cout << "Age: ";
cout << HeadPointer->Age;
cout << "nn";
HeadPointer = HeadPointer->Link;
usleep(75000);
}
while (HeadPointer != NULL);
cout << "Press enter to go to main menu: ";
cin.ignore(2);
cout << "n";
}
void SaveSettings(ofstream &FO, const PersonInfo *HeadPointer, const char *P)
{
FO.open(P);
if (FO.fail())
cout << "Couldn't Open Filen";
while (HeadPointer != NULL)
{
FO << HeadPointer->FirstName << endl;
FO << HeadPointer->LastName << endl;
FO << HeadPointer->Address << endl;
FO << HeadPointer->PhoneNumber << endl;
FO << HeadPointer->Age << endl << endl;
HeadPointer = HeadPointer->Link;
}
FO << (char) 0 << endl;
FO << "Date of Settings: " << Date() << endl;
FO.close();
}
void SortContacts(PersonInfo *&HeadPointer)
{
PersonInfo *MovingPointer1;//used to "crawl" down list
PersonInfo *MovingPointer2;//used to "crawl" down list
PersonInfo *StaticPointer;//always points at first node to give HeadPointer a way to link back to the list at end
PersonInfo *TemporaryPointer;//holds a node during a swap
bool ZeroSwapsOccured = false;//initialized at false to allow entrance into loop once
MovingPointer1 = StaticPointer = HeadPointer;//set all to point at first node
MovingPointer2 = HeadPointer->Link;
while (ZeroSwapsOccured == false)
{
ZeroSwapsOccured = true;
while (MovingPointer2->Link != NULL)
{
if (!NamesInOrder(MovingPointer1->LastName, MovingPointer2->LastName))
{
ZeroSwapsOccured = false;
//Temp = MP1
//MP1 = MP2
//MP2 = TEMP
MovingPointer1->Link = MovingPointer2->Link;
MovingPointer2->Link = MovingPointer1;
HeadPointer->Link = MovingPointer2;
}
}
}
HeadPointer = StaticPointer;//link HeadPointer back to list after sort
}
bool NamesInOrder(const char LastName1[], const char LastName2[])
{
for (int i = 0; LastName1[i] || LastName2[i]; ++i)//go until you get to the end of the larger name
{
if(toupper(LastName1[i]) < toupper(LastName2[i]))
return true;
if(toupper(LastName1[i]) > toupper(LastName2[i]))
return false;
}
return true;//this will only be used if same last name
//build in fucntionality to then go to first name after last name, if both last names are the same
}
string Date()//not my code here - just modified it to read easier
{
char Time[50];
time_t now = time(NULL);
strftime(Time, 50, "%b, %d, %Y", localtime(&now)); //short month name
return string(Time);
}
First-在这两种情况下,您都在重新排序列表。
第二次-交换两个节点通常需要五个操作:
- 将节点从第一个节点向后更改一个,以指向第二个节点
- 将节点从第二个节点更改回一个,以指向第一个节点
- 将第一个节点的
next
指针存储在临时指针中 - 将第一个节点的
next
指针更改为第二个节点的next
指针 - 将第二个节点的
next
指针更改为临时指针
交换两个变量至少需要三个操作:
- 将第一个变量存储在临时变量中
- 将第一个变量更改为第二个变量
- 将第二个变量更改为第一个变量
但现在将其乘以结构成员的数量。
该结构应该至少有2个数据成员——一个指针和一个有效负载——所以你马上就会看到至少6个操作。对于结构中的每个成员,它将增加3。因此,最好只是交换节点。
任何内存都不应该移动。链表中的节点在内存中不排序,而仅通过指向列表中下一个/上一个节点的指针相互关联。您的操作只需分配几个指针即可完成。
交换数据的成本更高,也更复杂。例如,要交换数据,您需要交换姓名、地址、号码、年龄等。
另一方面,交换节点意味着只交换列表中的两个内存位置地址。因此,交换节点是非常可取的。
其次,如果向节点添加更多的元数据字段,则不必更改排序代码来交换新添加的数据字段。
因此,您有一个要排序的链表。要正确高效地进行排序,您必须使用正确的排序算法,在本例中就是合并排序。当然,您不应该交换节点的数据。
检查此链接:http://www.geeksforgeeks.org/merge-sort-for-linked-list/
如果节点数据大小较大,那么最好交换节点的位置,而不是交换节点的数据(交换数据将是一个糟糕的选择)。
选择移动指针实现而不是交换数据的原因:
-
假设您想在一段时间后向联系人列表中添加一个新字段。如果交换数据,每次更改联系人列表字段时都必须更改代码。
-
随着联系人列表中字段的增加,交换数据的开销也会增加。
- 用于访问容器<T>数据成员的正确 API
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 数据成员SFINAE的C++17测试:gcc vs clang
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 如何在c++中定义以struct为数据成员的类中的构造函数
- 静态数据成员模板专用化的实例化点在哪里
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 使用指针访问数组中的对象数据成员
- 友元函数无法访问私有数据成员 (c++)
- 我可以在 C++ 中将数据成员/变量从其定义之外添加到结构中吗?
- 为什么将一个结构的引用设置为等于另一个结构只会更改一个数据成员?
- 将私有数据成员添加到野牛生成的类中
- 输入数据成员未按要求工作
- 二维矢量数据成员
- 在类 A 中创建类型为 B 类的向量 - 访问数据 [C++] [成员在两个类中都是私有的]
- 调用在 HXX 文件中声明的静态数据成员
- 是否可以根据其数据成员的类型确定类型的大小
- 排序链表-移动节点还是交换数据成员
- 当数组是类的数据成员时,如何使用指针交换数组
- 将非活动std::unique_ptr数据成员交换为联合