所以我在学习堆排序算法,我完全理解它。然而,当我查看我的代码时,我并不真正理解它为什么有效,因为在C#中,如果我想通过将参数传递给函数来更改参数的值,我必须使用ref关键字,否则它是一个值类型的参数,在传递给函数时不应更改。
public static class HeapSort
{
public static List<int> Sort(List<int> array)
{
List<int> result = new List<int>();
int n = array.Count;
for(int i = n/2 - 1; i >= 0; i--)
{
Heapify(array, n, i);
}
int sizeOfArray = n;
while(result.Count != n)
{
result.Insert(0, array[0]);
array[0] = array[sizeOfArray - 1];
array.RemoveAt(sizeOfArray - 1);
sizeOfArray--;
Heapify(array, sizeOfArray, 0);
}
return result;
}
private static void Heapify(List<int> array, int n, int i)
{
int smallest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if(left < n && array[smallest] > array[left])
{
smallest = left;
}
if(right < n && array[smallest] > array[right])
{
smallest = right;
}
if(smallest != i)
{
int temp = array[smallest];
array[smallest] = array[i];
array[i] = temp;
Heapify(array, n, smallest);
}
}
}
我不明白为什么在没有ref的情况下调用Heaify可以改变数组。
在c#中有引用类型和值类型。值类型是按值传递的,除非您使用ref
关键字传递它们。引用类型总是通过引用传递的。
例如,如果您声明两个变量,如
List<int> myList = new List<int>();
int myint = 0;
myInt
保持值0
,但myList
仅保持指向List<int>
的实际实例的引用。这意味着,当您将myInt
传递给一个方法时,您只需传递其值的一个副本。当您传递myList
时,您会执行相同的操作——您传递其值的副本,但其值是对List<int>
实例的引用,因此该引用的副本仍然引用堆上的相同List<int>
。
当您传递带有ref
关键字的变量时,实际上您传递了一个指向原始变量的指针。如果用ref
关键字传递myInt
,则方法将收到指向myInt
的指针,因此它将能够修改其实际值。若用ref
关键字传递myList
,同样的事情也会发生。现在,您的方法可以修改myList
变量的实际值。它可以将其设置为其他引用。
当你在实践中看到它时,它可能更容易理解,所以这里有一个小的控制台应用程序,显示了两种方法之间的区别,这两种方法的区别仅在于使用ref
关键字
static void Main(string[] args)
{
//create new instane of List<int> on the heap and store reference to it on the stack in 'myList 'variable
List<int> myList = new List<int>() { 1 };
//create new instance of int and store it on the stack in 'myInt' variable
int myint = 2;
//call MyMethod
//copy value of myInt (2) to the new stack frame and store it in 'i' variable
//copy value of myList (reference to List<int>) to the new stack frame and store it in 'list' variable
MyMethod(myint, myList);
Console.WriteLine(myint); //prints 2
Console.WriteLine(myList[0]); //prints 4
//call MyMethod
//inside new stack frame store pointer to 'myint' variable
//inside new stack frame store pointer to 'myList' variable
MyMethod(ref myint, ref myList);
Console.WriteLine(myint); //prints 3
Console.WriteLine(myList[0]); //prints 5
Console.ReadLine();
}
static void MyMethod(int i, List<int> list)
{
//store value of 3 on stack in variable 'i'
i = 3;
//use reference stored on stack in 'list' variable to find our instance of List<int> on the heap and store 4 under index 0
list[0] = 4;
//create new instane of List<int> on the heap and store reference to it on the stack, in 'list 'variable
list = new List<int>() { 5 };
}
static void MyMethod(ref int i, ref List<int> list)
{
//use pointer to 'myInt' variable to store value of 3 in 'myInt' variable
i = 3;
//use pointer to 'myList' variable to get reference stored in it and use that reference to find our instance of List<int> on the heap and store 4 under index 0
list[0] = 4;
//create new instane of List<int> on the heap and use pointer to 'myList' variable to store reference to that new instance inside 'myList' variable
list = new List<int>() { 5 };
}