📄 sort.c
字号:
/*
* These functions sort lines according to keys in a marked BOX block.
*
* Being that the data structure was changed from a big text buffer to a
* linked list, we can replace the simple insertion sort algorithm with a
* much faster sort algorithm. The classic search and sort reference is by
* Donald E. Knuth, _The Art of Computer Programming; Volume 3: Sorting and
* Searching_. One of the fastest and most widely used general purpose
* sorting algorithms is the "Quicksort" algorithm.
*
* For implementation hints and guidelines on the Quicksort algorithm, one
* might read the original Quicksort paper by C. A. R. Hoare or anything
* by Robert Sedgewick. Although Dr. Sedgewick includes a chapter on
* Quicksort in his intro computer science textbooks, _Algorithms in X_,
* I prefer the detailed hints and guidelines in his 1978 CACM paper.
* Incidentally, Dr. Sedgewick's Ph.D. dissertation was on the modification
* and mathematical analysis of the Quicksort algorithm.
*
*
* See:
*
* Charles Antony Richard Hoare, "Algorithm 63: Partition." _Communications
* of the ACM_, 4 (No. 7): 321, 1961.
*
* Charles Antony Richard Hoare, "Algorithm 64: Quicksort." _Communications
* of the ACM_, 4 (No. 7): 321, 1961.
*
* Charles Antony Richard Hoare, "Quicksort." _The Computer Journal_,
* 5 (April 1962 - January 1963): 10-15, 1962.
*
* See also:
*
* Donald Ervin Knuth, _The Art of Computer Programming; Volume 3: Sorting
* and Searching_, Addison-Wesley, Reading, Mass., 1973, Chapter 5,
* Sorting, pp 114-123. ISBN 0-201-03803-X.
*
* Robert Sedgewick, "Implementing Quicksort Programs." _Communications
* of the ACM_, 21 (No. 10): 847-857, 1978.
*
*
* Quicksort in TDE
*
* Quicksort in TDE is a stable, non-recursive implementation based on
* Program 2, page 851, CACM, 21 (No. 10), 1978, by Robert Sedgewick. My
* partition algorithm finds the median-of-three. Then, it walks from the
* head of the list, comparing nodes, and uses the first occurrence of the
* key of the median-of-three as the partition node. Mostly by accident
* and partly by design, my partition routine uses a "fat pivot" to keep
* equal nodes in the same relative order as they appear in the file (the
* definition of a stable sort). By using the first median-of-three node
* as the partitioning node, 1) sorting a sorted list is fairly fast and
* 2) encountering a worst case is very unlikely. TDE will sort, fairly
* fast, multiple keys ascending or descending, ignore or match case, and
* preserve order in the file, while using a custom sort sequence for your
* favorite domestic or foreign language.
*
*
* New editor name: TDE, the Thomson-Davis Editor.
* Author: Frank Davis
* Date: June 5, 1991, version 1.0
* Date: July 29, 1991, version 1.1
* Date: October 5, 1991, version 1.2
* Date: January 20, 1992, version 1.3
* Date: February 17, 1992, version 1.4
* Date: April 1, 1992, version 1.5
* Date: June 5, 1992, version 2.0
* Date: October 31, 1992, version 2.1
* Date: April 1, 1993, version 2.2
* Date: June 5, 1993, version 3.0
*
* This code is released into the public domain, Frank Davis.
* You may use and distribute it freely.
*/
#include "tdestr.h"
#include "common.h"
#include "tdefunc.h"
#include "define.h"
/*
* Name: sort_box_block
* Purpose: sort lines according to text in marked BOX block
* Date: June 5, 1992
* Passed: window: pointer to current window
* Notes: quick sort and insertion sort the lines in the BOX buff according
* to stuff in a box block.
*/
int sort_box_block( WINDOW *window )
{
int prompt_line;
int block_type;
line_list_ptr ll;
register file_infos *file;
WINDOW *sw;
int rc;
char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute */
/*
* make sure block is marked OK
*/
rc = OK;
prompt_line = window->bottom_line;
entab_linebuff( );
if (un_copy_line( window->ll, window, TRUE ) == ERROR)
return( ERROR );
check_block( );
if (g_status.marked == TRUE) {
file = g_status.marked_file;
block_type = file->block_type;
if (block_type == BOX) {
/*
* sort ascending or descending?
*/
rc = get_sort_order( window );
if (rc != ERROR) {
file->modified = TRUE;
if (mode.do_backups == TRUE) {
sw = g_status.window_list;
for (; ptoul( sw->file_info ) != ptoul( file );)
sw = sw->next;
backup_file( sw );
}
/*
* figure the width of the block.
*/
sort.block_len = file->block_ec + 1 - file->block_bc;
/*
* save the prompt line and print the quicksort message.
*/
save_screen_line( 0, prompt_line, line_buff );
eol_clear( 0, prompt_line, g_display.text_color );
set_prompt( block22a, prompt_line );
/*
* set up the sort structure.
*/
sort.bc = g_status.marked_file->block_bc;
sort.ec = g_status.marked_file->block_ec;
sort.order_array = (mode.search_case == IGNORE) ?
sort_order.ignore : sort_order.match;
/*
* save the previous node for use with insertion sort.
*/
ll = file->block_start->prev;
quick_sort_block( file->block_br, file->block_er,
file->block_start, file->block_end );
/*
* get back previous node and clean up list with insertion
* sort.
*/
if (ll == NULL)
ll = file->line_list;
else
ll = ll->next;
set_prompt( block22b, prompt_line );
insertion_sort_block( file->block_br, file->block_er, ll );
/*
* housekeeping. mark the file as dirty and restore the
* cursors, which are scrambled during the sort.
*/
file->dirty = GLOBAL;
restore_cursors( file );
restore_screen_line( 0, prompt_line, line_buff );
}
} else {
/*
* can only sort box blocks
*/
error( WARNING, prompt_line, block23 );
rc = ERROR;
}
} else {
/*
* box not marked
*/
error( WARNING, prompt_line, block24 );
rc = ERROR;
}
return( rc );
}
/*
* Name: quick_sort_block
* Purpose: sort lines according to text in marked BOX block
* Date: Jaunary 10, 1993
* Passed: low: starting line in box block
* high: ending line in a box block
* low_node: starting node in box block
* high_node: ending node in box block
* Notes: Quicksort lines in the BOX block according to keys in
* a box block.
* because the median of three method is used to find the partion
* node, high - low should be greater than or equal to 2.
* with end recursion removal and sorting the smallest sublist
* first, our stack only needs room for log2 (N+1)/(M+2) nodes.
* a stack size of 24 can reliably handle almost 500 million lines.
*/
void quick_sort_block( long low, long high, line_list_ptr low_node,
line_list_ptr high_node )
{
long low_rline_stack[24];
long high_rline_stack[24];
line_list_ptr low_node_stack[24];
line_list_ptr high_node_stack[24];
long low_count;
long high_count;
long count;
line_list_ptr low_start;
line_list_ptr low_head;
line_list_ptr low_tail;
line_list_ptr high_end;
line_list_ptr high_head;
line_list_ptr high_tail;
line_list_ptr equal_head;
line_list_ptr equal_tail;
line_list_ptr walk_node;
line_list_ptr median_node;
int i;
int stack_pointer;
assert( low_node->len != EOF);
assert( high_node->len != EOF);
stack_pointer = 0;
while (TRUE) {
/*
* being that a median-of-three is used as the partition algorithm,
* we probably need to have at least 2 nodes in each sublist. I
* chose a minimum of 25 nodes as a SWAG (scientific wild ass guess).
* a simple insertion sort mops the list after quicksort finishes.
*/
while (high - low > 25) {
assert( high >= 1 );
assert( low >= 1 );
assert( low <= high );
/*
* start the walk node at the head of the list and walk to the
* middle of the sublist.
*/
walk_node = low_node;
count = (high - low) / 2;
for (; count > 0; count--)
walk_node = walk_node->next;
/*
* now, find the median of the low, middle, and high node.
*
* being that I am subject to error, let's assert that we really
* did find the median-of-three.
*/
load_pivot( low_node );
if (compare_pivot( walk_node ) < 0) {
low_head = walk_node;
median_node = low_node;
} else {
low_head = low_node;
median_node = walk_node;
}
high_head = high_node;
load_pivot( median_node );
if (compare_pivot( high_node ) < 0) {
high_head = median_node;
median_node = high_node;
}
load_pivot( median_node );
if (compare_pivot( low_head ) > 0) {
low_tail = median_node;
median_node = low_head;
low_head = low_tail;
}
load_pivot( median_node );
assert( compare_pivot( low_head ) <= 0 );
assert( compare_pivot( high_head ) >= 0 );
/*
* now, walk again from the head of the list comparing nodes and
* use the first occurrence of the median node as the partition.
*/
walk_node = low_node;
for (i = 0; ; walk_node = walk_node->next) {
if (compare_pivot( walk_node ) == 0)
break;
i = 1;
}
/*
* initialize pointers and counters for this partition.
*/
low_start = low_node->prev;
high_end = high_node->next;
low_head = low_tail = NULL;
high_head = high_tail = NULL;
low_count = high_count = 0;
/*
* setup the first occurrence of the median node as a "fat pivot"
* sublist. there are two cases to consider 1) the first
* occurrence of the median node is the first element in the
* sublist, i == 0, or 2) the first occurrence of the median node
* is somewhere in the sublist.
*/
if (i == 0)
walk_node = equal_head = equal_tail = low_node;
else {
equal_head = equal_tail = walk_node;
equal_head->next->prev = equal_head->prev;
equal_head->prev->next = equal_head->next;
equal_head->next = low_node;
walk_node = equal_head;
}
load_pivot( equal_head );
/*
* PARTITION:
* put all nodes less than the pivot on the end of the low list.
* put all nodes equal to the pivot on the end of the equal list.
* put all nodes greater than the pivot on the end of the high list.
*/
for (count=low+1; count <= high; count++) {
walk_node = walk_node->next;
i = compare_pivot( walk_node );
if (i > 0) {
if (high_head == NULL)
high_head = high_tail = walk_node;
else {
high_tail->next = walk_node;
walk_node->prev = high_tail;
high_tail = walk_node;
}
/*
* keep a count of the number of nodes in the high list.
*/
++high_count;
} else if (i < 0) {
if (low_head == NULL)
low_head = low_tail = walk_node;
else {
low_tail->next = walk_node;
walk_node->prev = low_tail;
low_tail = walk_node;
}
/*
* keep a count of the number of nodes in the low list
*/
++low_count;
} else {
equal_tail->next = walk_node;
walk_node->prev = equal_tail;
equal_tail = walk_node;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -